1
0

Changes to handling of refunds in Anrok (#4556)

* Use negations, track transaction version/accounting time, use original charge accounting time in refunds

* query cache

* chore: query cache, clippy, fmt

* Fix tax drift calculation

* Fix migration

* Increase update_tax_transactions rate
This commit is contained in:
François-Xavier Talbot
2025-10-17 16:57:36 +01:00
committed by GitHub
parent b23d3e674f
commit 5db5bf4c4c
18 changed files with 530 additions and 318 deletions

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\"\n FROM charges\n \n WHERE\n charge_type = $1 AND\n status = 'failed' AND due < NOW() - INTERVAL '30 days'\n ",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\",\n charges.tax_transaction_version AS \"tax_transaction_version?\",\n charges.tax_platform_accounting_time AS \"tax_platform_accounting_time?\"\n FROM charges\n \n INNER JOIN users_subscriptions us ON us.id = charges.subscription_id\n WHERE\n charges.charge_type = $1 AND\n (\n (charges.status = 'cancelled' AND charges.due < NOW()) OR\n (charges.status = 'expiring' AND charges.due < NOW()) OR\n (charges.status = 'failed' AND charges.last_attempt < NOW() - INTERVAL '2 days')\n )\n AND us.status = 'provisioned'\n ",
"describe": {
"columns": [
{
@@ -97,6 +97,16 @@
"ordinal": 18,
"name": "tax_drift_loss?",
"type_info": "Int8"
},
{
"ordinal": 19,
"name": "tax_transaction_version?",
"type_info": "Int4"
},
{
"ordinal": 20,
"name": "tax_platform_accounting_time?",
"type_info": "Timestamptz"
}
],
"parameters": {
@@ -123,8 +133,10 @@
true,
true,
true,
true,
true,
true
]
},
"hash": "e36e0ac1e2edb73533961a18e913f0b8e4f420a76e511571bb2eed9355771e54"
"hash": "372c03a6daf0045f615faa9a6205558cd0ea1d9dba5948e8fa2496ed99de8fea"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\"\n FROM charges\n WHERE parent_charge_id = $1",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\",\n charges.tax_transaction_version AS \"tax_transaction_version?\",\n charges.tax_platform_accounting_time AS \"tax_platform_accounting_time?\"\n FROM charges\n WHERE user_id = $1 ORDER BY due DESC",
"describe": {
"columns": [
{
@@ -97,6 +97,16 @@
"ordinal": 18,
"name": "tax_drift_loss?",
"type_info": "Int8"
},
{
"ordinal": 19,
"name": "tax_transaction_version?",
"type_info": "Int4"
},
{
"ordinal": 20,
"name": "tax_platform_accounting_time?",
"type_info": "Timestamptz"
}
],
"parameters": {
@@ -123,8 +133,10 @@
true,
true,
true,
true,
true,
true
]
},
"hash": "51c542076b4b3811eb12f051294f55827a27f51e65e668525b8b545f570c0bda"
"hash": "4a0e5c7ebd4565b95fb99983484cec76952f1505a75eb1a006b3ad9b8aa91a51"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\"\n FROM charges\n \n INNER JOIN users_subscriptions us ON us.id = charges.subscription_id\n WHERE\n charges.charge_type = $1 AND\n (\n (charges.status = 'cancelled' AND charges.due < NOW()) OR\n (charges.status = 'expiring' AND charges.due < NOW()) OR\n (charges.status = 'failed' AND charges.last_attempt < NOW() - INTERVAL '2 days')\n )\n AND us.status = 'provisioned'\n ",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\",\n charges.tax_transaction_version AS \"tax_transaction_version?\",\n charges.tax_platform_accounting_time AS \"tax_platform_accounting_time?\"\n FROM charges\n \n WHERE\n charge_type = $1 AND\n (\n (status = 'open' AND due < NOW()) OR\n (status = 'failed' AND last_attempt < NOW() - INTERVAL '2 days')\n )\n ",
"describe": {
"columns": [
{
@@ -97,6 +97,16 @@
"ordinal": 18,
"name": "tax_drift_loss?",
"type_info": "Int8"
},
{
"ordinal": 19,
"name": "tax_transaction_version?",
"type_info": "Int4"
},
{
"ordinal": 20,
"name": "tax_platform_accounting_time?",
"type_info": "Timestamptz"
}
],
"parameters": {
@@ -123,8 +133,10 @@
true,
true,
true,
true,
true,
true
]
},
"hash": "9f0c73fabe99d9891faaebdd3518b362437dcdcef9cd9a68b950fba61218bb4d"
"hash": "4ed57832b7c02e1f4c683e256455c76e645cde49f95b0e5bfecd3d3d2330ed5c"
}

View File

@@ -0,0 +1,34 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO charges (id, user_id, price_id, amount, currency_code, charge_type, status, due, last_attempt, subscription_id, subscription_interval, payment_platform, payment_platform_id, parent_charge_id, net, tax_amount, tax_platform_id, tax_last_updated, tax_drift_loss, tax_transaction_version, tax_platform_accounting_time)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)\n ON CONFLICT (id)\n DO UPDATE\n SET status = EXCLUDED.status,\n last_attempt = EXCLUDED.last_attempt,\n due = EXCLUDED.due,\n subscription_id = EXCLUDED.subscription_id,\n subscription_interval = EXCLUDED.subscription_interval,\n payment_platform = EXCLUDED.payment_platform,\n payment_platform_id = EXCLUDED.payment_platform_id,\n parent_charge_id = EXCLUDED.parent_charge_id,\n net = EXCLUDED.net,\n tax_amount = EXCLUDED.tax_amount,\n tax_platform_id = EXCLUDED.tax_platform_id,\n tax_last_updated = EXCLUDED.tax_last_updated,\n price_id = EXCLUDED.price_id,\n amount = EXCLUDED.amount,\n currency_code = EXCLUDED.currency_code,\n charge_type = EXCLUDED.charge_type,\n\t\t\t\t\ttax_drift_loss = EXCLUDED.tax_drift_loss,\n\t\t\t\t\ttax_transaction_version = EXCLUDED.tax_transaction_version,\n\t\t\t\t\ttax_platform_accounting_time = EXCLUDED.tax_platform_accounting_time\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8",
"Int8",
"Int8",
"Int8",
"Text",
"Text",
"Varchar",
"Timestamptz",
"Timestamptz",
"Int8",
"Text",
"Text",
"Text",
"Int8",
"Int8",
"Int8",
"Text",
"Timestamptz",
"Int8",
"Int4",
"Timestamptz"
]
},
"nullable": []
},
"hash": "5a972c49ccacf8735ec36d691f1c34b86c33703f984c869346b0c0be1c4a4883"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\"\n FROM charges\n WHERE id = $1",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\",\n charges.tax_transaction_version AS \"tax_transaction_version?\",\n charges.tax_platform_accounting_time AS \"tax_platform_accounting_time?\"\n FROM charges\n WHERE parent_charge_id = $1",
"describe": {
"columns": [
{
@@ -97,6 +97,16 @@
"ordinal": 18,
"name": "tax_drift_loss?",
"type_info": "Int8"
},
{
"ordinal": 19,
"name": "tax_transaction_version?",
"type_info": "Int4"
},
{
"ordinal": 20,
"name": "tax_platform_accounting_time?",
"type_info": "Timestamptz"
}
],
"parameters": {
@@ -123,8 +133,10 @@
true,
true,
true,
true,
true,
true
]
},
"hash": "4e8e9f9cb42f90cc17702386fdb78385608f19dae9439cb6a860503600127b04"
"hash": "64233913683d187ee6c449eb106bd1a27929e05b497aaea93e9e8f318770c64c"
}

View File

@@ -0,0 +1,143 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\",\n charges.tax_transaction_version AS \"tax_transaction_version?\",\n charges.tax_platform_accounting_time AS \"tax_platform_accounting_time?\"\n FROM charges\n \n\t\t\tWHERE\n\t\t\t status = 'succeeded'\n\t\t\t AND tax_platform_id IS NULL\n AND payment_platform_id IS NOT NULL\n\t\t\tORDER BY due ASC\n\t\t\tFOR NO KEY UPDATE SKIP LOCKED\n OFFSET $1\n\t\t\tLIMIT $2\n\t\t\t",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "user_id",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "price_id",
"type_info": "Int8"
},
{
"ordinal": 3,
"name": "amount",
"type_info": "Int8"
},
{
"ordinal": 4,
"name": "currency_code",
"type_info": "Text"
},
{
"ordinal": 5,
"name": "status",
"type_info": "Varchar"
},
{
"ordinal": 6,
"name": "due",
"type_info": "Timestamptz"
},
{
"ordinal": 7,
"name": "last_attempt",
"type_info": "Timestamptz"
},
{
"ordinal": 8,
"name": "charge_type",
"type_info": "Text"
},
{
"ordinal": 9,
"name": "subscription_id",
"type_info": "Int8"
},
{
"ordinal": 10,
"name": "tax_amount",
"type_info": "Int8"
},
{
"ordinal": 11,
"name": "tax_platform_id",
"type_info": "Text"
},
{
"ordinal": 12,
"name": "subscription_interval?",
"type_info": "Text"
},
{
"ordinal": 13,
"name": "payment_platform",
"type_info": "Text"
},
{
"ordinal": 14,
"name": "payment_platform_id?",
"type_info": "Text"
},
{
"ordinal": 15,
"name": "parent_charge_id?",
"type_info": "Int8"
},
{
"ordinal": 16,
"name": "net?",
"type_info": "Int8"
},
{
"ordinal": 17,
"name": "tax_last_updated?",
"type_info": "Timestamptz"
},
{
"ordinal": 18,
"name": "tax_drift_loss?",
"type_info": "Int8"
},
{
"ordinal": 19,
"name": "tax_transaction_version?",
"type_info": "Int4"
},
{
"ordinal": 20,
"name": "tax_platform_accounting_time?",
"type_info": "Timestamptz"
}
],
"parameters": {
"Left": [
"Int8",
"Int8"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
true,
false,
true,
false,
true,
true,
false,
true,
true,
true,
true,
true,
true,
true
]
},
"hash": "70236e8be98967070160f703ed0242239eb5a4c6bef3748dac57fa339260c9c1"
}

View File

@@ -1,130 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\"\n FROM charges\n WHERE subscription_id = $1 AND (status = 'open' OR status = 'expiring' OR status = 'cancelled' OR status = 'failed')",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "user_id",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "price_id",
"type_info": "Int8"
},
{
"ordinal": 3,
"name": "amount",
"type_info": "Int8"
},
{
"ordinal": 4,
"name": "currency_code",
"type_info": "Text"
},
{
"ordinal": 5,
"name": "status",
"type_info": "Varchar"
},
{
"ordinal": 6,
"name": "due",
"type_info": "Timestamptz"
},
{
"ordinal": 7,
"name": "last_attempt",
"type_info": "Timestamptz"
},
{
"ordinal": 8,
"name": "charge_type",
"type_info": "Text"
},
{
"ordinal": 9,
"name": "subscription_id",
"type_info": "Int8"
},
{
"ordinal": 10,
"name": "tax_amount",
"type_info": "Int8"
},
{
"ordinal": 11,
"name": "tax_platform_id",
"type_info": "Text"
},
{
"ordinal": 12,
"name": "subscription_interval?",
"type_info": "Text"
},
{
"ordinal": 13,
"name": "payment_platform",
"type_info": "Text"
},
{
"ordinal": 14,
"name": "payment_platform_id?",
"type_info": "Text"
},
{
"ordinal": 15,
"name": "parent_charge_id?",
"type_info": "Int8"
},
{
"ordinal": 16,
"name": "net?",
"type_info": "Int8"
},
{
"ordinal": 17,
"name": "tax_last_updated?",
"type_info": "Timestamptz"
},
{
"ordinal": 18,
"name": "tax_drift_loss?",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
true,
false,
true,
false,
true,
true,
false,
true,
true,
true,
true,
true
]
},
"hash": "9a35729acbba06eafaa205922e4987e082a000ec1b397957650e1332191613ca"
}

View File

@@ -1,32 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO charges (id, user_id, price_id, amount, currency_code, charge_type, status, due, last_attempt, subscription_id, subscription_interval, payment_platform, payment_platform_id, parent_charge_id, net, tax_amount, tax_platform_id, tax_last_updated, tax_drift_loss)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)\n ON CONFLICT (id)\n DO UPDATE\n SET status = EXCLUDED.status,\n last_attempt = EXCLUDED.last_attempt,\n due = EXCLUDED.due,\n subscription_id = EXCLUDED.subscription_id,\n subscription_interval = EXCLUDED.subscription_interval,\n payment_platform = EXCLUDED.payment_platform,\n payment_platform_id = EXCLUDED.payment_platform_id,\n parent_charge_id = EXCLUDED.parent_charge_id,\n net = EXCLUDED.net,\n tax_amount = EXCLUDED.tax_amount,\n tax_platform_id = EXCLUDED.tax_platform_id,\n tax_last_updated = EXCLUDED.tax_last_updated,\n price_id = EXCLUDED.price_id,\n amount = EXCLUDED.amount,\n currency_code = EXCLUDED.currency_code,\n charge_type = EXCLUDED.charge_type,\n\t\t\t\t\ttax_drift_loss = EXCLUDED.tax_drift_loss\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8",
"Int8",
"Int8",
"Int8",
"Text",
"Text",
"Varchar",
"Timestamptz",
"Timestamptz",
"Int8",
"Text",
"Text",
"Text",
"Int8",
"Int8",
"Int8",
"Text",
"Timestamptz",
"Int8"
]
},
"nullable": []
},
"hash": "c0c70ebc3d59a5ab6a4c81e987df178a7828a45ed3134d3336cb59572f40beab"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\"\n FROM charges\n \n WHERE\n charge_type = $1 AND\n (\n (status = 'open' AND due < NOW()) OR\n (status = 'failed' AND last_attempt < NOW() - INTERVAL '2 days')\n )\n ",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\",\n charges.tax_transaction_version AS \"tax_transaction_version?\",\n charges.tax_platform_accounting_time AS \"tax_platform_accounting_time?\"\n FROM charges\n \n WHERE\n charge_type = $1 AND\n status = 'failed' AND due < NOW() - INTERVAL '30 days'\n ",
"describe": {
"columns": [
{
@@ -97,6 +97,16 @@
"ordinal": 18,
"name": "tax_drift_loss?",
"type_info": "Int8"
},
{
"ordinal": 19,
"name": "tax_transaction_version?",
"type_info": "Int4"
},
{
"ordinal": 20,
"name": "tax_platform_accounting_time?",
"type_info": "Timestamptz"
}
],
"parameters": {
@@ -123,8 +133,10 @@
true,
true,
true,
true,
true,
true
]
},
"hash": "e2e58113bc3a3db6ffc75b5c5e10acd16403aa0679ef53330f2ce3e8a45f7b9f"
"hash": "caf16ed13389398c1ee3456c0e2534310be545293b2693f1c747425295b367a8"
}

View File

@@ -1,130 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\"\n FROM charges\n \n\t\t\tINNER JOIN users u ON u.id = charges.user_id\n\t\t\tWHERE\n\t\t\t status = 'open'\n\t\t\t AND COALESCE(tax_last_updated, '-infinity' :: TIMESTAMPTZ) < NOW() - INTERVAL '1 day'\n\t\t\t AND u.email IS NOT NULL\n\t\t\t AND due - INTERVAL '7 days' > NOW()\n AND due - INTERVAL '14 days' < NOW() -- Due between 7 and 14 days from now\n\t\t\tORDER BY COALESCE(tax_last_updated, '-infinity' :: TIMESTAMPTZ) ASC\n\t\t\tFOR NO KEY UPDATE SKIP LOCKED\n\t\t\tLIMIT $1\n\t\t\t",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "user_id",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "price_id",
"type_info": "Int8"
},
{
"ordinal": 3,
"name": "amount",
"type_info": "Int8"
},
{
"ordinal": 4,
"name": "currency_code",
"type_info": "Text"
},
{
"ordinal": 5,
"name": "status",
"type_info": "Varchar"
},
{
"ordinal": 6,
"name": "due",
"type_info": "Timestamptz"
},
{
"ordinal": 7,
"name": "last_attempt",
"type_info": "Timestamptz"
},
{
"ordinal": 8,
"name": "charge_type",
"type_info": "Text"
},
{
"ordinal": 9,
"name": "subscription_id",
"type_info": "Int8"
},
{
"ordinal": 10,
"name": "tax_amount",
"type_info": "Int8"
},
{
"ordinal": 11,
"name": "tax_platform_id",
"type_info": "Text"
},
{
"ordinal": 12,
"name": "subscription_interval?",
"type_info": "Text"
},
{
"ordinal": 13,
"name": "payment_platform",
"type_info": "Text"
},
{
"ordinal": 14,
"name": "payment_platform_id?",
"type_info": "Text"
},
{
"ordinal": 15,
"name": "parent_charge_id?",
"type_info": "Int8"
},
{
"ordinal": 16,
"name": "net?",
"type_info": "Int8"
},
{
"ordinal": 17,
"name": "tax_last_updated?",
"type_info": "Timestamptz"
},
{
"ordinal": 18,
"name": "tax_drift_loss?",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
true,
false,
true,
false,
true,
true,
false,
true,
true,
true,
true,
true
]
},
"hash": "cd18ae8abe81a159a134923957f4cd6d0ba3b1bcbc89df349cf7b5b1897603b8"
}

View File

@@ -0,0 +1,142 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\",\n charges.tax_transaction_version AS \"tax_transaction_version?\",\n charges.tax_platform_accounting_time AS \"tax_platform_accounting_time?\"\n FROM charges\n \n\t\t\tINNER JOIN users u ON u.id = charges.user_id\n\t\t\tWHERE\n\t\t\t status = 'open'\n\t\t\t AND COALESCE(tax_last_updated, '-infinity' :: TIMESTAMPTZ) < NOW() - INTERVAL '1 day'\n\t\t\t AND u.email IS NOT NULL\n\t\t\t AND due - INTERVAL '7 days' > NOW()\n AND due - INTERVAL '14 days' < NOW() -- Due between 7 and 14 days from now\n\t\t\tORDER BY COALESCE(tax_last_updated, '-infinity' :: TIMESTAMPTZ) ASC\n\t\t\tFOR NO KEY UPDATE SKIP LOCKED\n\t\t\tLIMIT $1\n\t\t\t",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "user_id",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "price_id",
"type_info": "Int8"
},
{
"ordinal": 3,
"name": "amount",
"type_info": "Int8"
},
{
"ordinal": 4,
"name": "currency_code",
"type_info": "Text"
},
{
"ordinal": 5,
"name": "status",
"type_info": "Varchar"
},
{
"ordinal": 6,
"name": "due",
"type_info": "Timestamptz"
},
{
"ordinal": 7,
"name": "last_attempt",
"type_info": "Timestamptz"
},
{
"ordinal": 8,
"name": "charge_type",
"type_info": "Text"
},
{
"ordinal": 9,
"name": "subscription_id",
"type_info": "Int8"
},
{
"ordinal": 10,
"name": "tax_amount",
"type_info": "Int8"
},
{
"ordinal": 11,
"name": "tax_platform_id",
"type_info": "Text"
},
{
"ordinal": 12,
"name": "subscription_interval?",
"type_info": "Text"
},
{
"ordinal": 13,
"name": "payment_platform",
"type_info": "Text"
},
{
"ordinal": 14,
"name": "payment_platform_id?",
"type_info": "Text"
},
{
"ordinal": 15,
"name": "parent_charge_id?",
"type_info": "Int8"
},
{
"ordinal": 16,
"name": "net?",
"type_info": "Int8"
},
{
"ordinal": 17,
"name": "tax_last_updated?",
"type_info": "Timestamptz"
},
{
"ordinal": 18,
"name": "tax_drift_loss?",
"type_info": "Int8"
},
{
"ordinal": 19,
"name": "tax_transaction_version?",
"type_info": "Int4"
},
{
"ordinal": 20,
"name": "tax_platform_accounting_time?",
"type_info": "Timestamptz"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
true,
false,
true,
false,
true,
true,
false,
true,
true,
true,
true,
true,
true,
true
]
},
"hash": "ce23f89106ef7b34f5a935f6e792d87b8805e87ac0cefb43828fc6d3aca52399"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\"\n FROM charges\n WHERE user_id = $1 ORDER BY due DESC",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\",\n charges.tax_transaction_version AS \"tax_transaction_version?\",\n charges.tax_platform_accounting_time AS \"tax_platform_accounting_time?\"\n FROM charges\n WHERE subscription_id = $1 AND (status = 'open' OR status = 'expiring' OR status = 'cancelled' OR status = 'failed')",
"describe": {
"columns": [
{
@@ -97,6 +97,16 @@
"ordinal": 18,
"name": "tax_drift_loss?",
"type_info": "Int8"
},
{
"ordinal": 19,
"name": "tax_transaction_version?",
"type_info": "Int4"
},
{
"ordinal": 20,
"name": "tax_platform_accounting_time?",
"type_info": "Timestamptz"
}
],
"parameters": {
@@ -123,8 +133,10 @@
true,
true,
true,
true,
true,
true
]
},
"hash": "7973e569e784f416c1b4f1e6f3b099dca9c0d9c84e55951a730d8c214580e0d6"
"hash": "ead967d7a8e268a583eb44900a5ef7c45548b4cf3d8cb6545aad801f9fcc5a56"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\"\n FROM charges\n \n\t\t\tWHERE\n\t\t\t status = 'succeeded'\n\t\t\t AND tax_platform_id IS NULL\n AND payment_platform_id IS NOT NULL\n\t\t\tORDER BY due ASC\n\t\t\tFOR NO KEY UPDATE SKIP LOCKED\n OFFSET $1\n\t\t\tLIMIT $2\n\t\t\t",
"query": "\n SELECT\n charges.id, charges.user_id, charges.price_id, charges.amount, charges.currency_code, charges.status, charges.due, charges.last_attempt,\n charges.charge_type, charges.subscription_id, charges.tax_amount, charges.tax_platform_id,\n -- Workaround for https://github.com/launchbadge/sqlx/issues/3336\n charges.subscription_interval AS \"subscription_interval?\",\n charges.payment_platform,\n charges.payment_platform_id AS \"payment_platform_id?\",\n charges.parent_charge_id AS \"parent_charge_id?\",\n charges.net AS \"net?\",\n\t\t\t\tcharges.tax_last_updated AS \"tax_last_updated?\",\n\t\t\t\tcharges.tax_drift_loss AS \"tax_drift_loss?\",\n charges.tax_transaction_version AS \"tax_transaction_version?\",\n charges.tax_platform_accounting_time AS \"tax_platform_accounting_time?\"\n FROM charges\n WHERE id = $1",
"describe": {
"columns": [
{
@@ -97,11 +97,20 @@
"ordinal": 18,
"name": "tax_drift_loss?",
"type_info": "Int8"
},
{
"ordinal": 19,
"name": "tax_transaction_version?",
"type_info": "Int4"
},
{
"ordinal": 20,
"name": "tax_platform_accounting_time?",
"type_info": "Timestamptz"
}
],
"parameters": {
"Left": [
"Int8",
"Int8"
]
},
@@ -124,8 +133,10 @@
true,
true,
true,
true,
true,
true
]
},
"hash": "050e755134f6d1f09de805ae2cd0f7ca8f6efb96be9f070c43db7fd2049af2d2"
"hash": "f0618e69765ba605b1db7f25a233cbf6f13bddc80a9719cbd401395db718b2f2"
}

View File

@@ -0,0 +1,2 @@
ALTER TABLE charges ADD COLUMN tax_transaction_version INTEGER;
ALTER TABLE charges ADD COLUMN tax_platform_accounting_time TIMESTAMP WITH TIME ZONE;

View File

@@ -30,6 +30,8 @@ pub struct DBCharge {
pub tax_amount: i64,
pub tax_platform_id: Option<String>,
pub tax_last_updated: Option<DateTime<Utc>>,
pub tax_transaction_version: Option<i32>,
pub tax_platform_accounting_time: Option<DateTime<Utc>>,
// Net is always in USD
pub net: Option<i64>,
@@ -56,6 +58,8 @@ struct ChargeQueryResult {
tax_last_updated: Option<DateTime<Utc>>,
net: Option<i64>,
tax_drift_loss: Option<i64>,
tax_transaction_version: Option<i32>,
tax_platform_accounting_time: Option<DateTime<Utc>>,
}
impl TryFrom<ChargeQueryResult> for DBCharge {
@@ -84,6 +88,8 @@ impl TryFrom<ChargeQueryResult> for DBCharge {
net: r.net,
tax_last_updated: r.tax_last_updated,
tax_drift_loss: r.tax_drift_loss,
tax_transaction_version: r.tax_transaction_version,
tax_platform_accounting_time: r.tax_platform_accounting_time,
})
}
}
@@ -103,7 +109,9 @@ macro_rules! select_charges_with_predicate {
charges.parent_charge_id AS "parent_charge_id?",
charges.net AS "net?",
charges.tax_last_updated AS "tax_last_updated?",
charges.tax_drift_loss AS "tax_drift_loss?"
charges.tax_drift_loss AS "tax_drift_loss?",
charges.tax_transaction_version AS "tax_transaction_version?",
charges.tax_platform_accounting_time AS "tax_platform_accounting_time?"
FROM charges
"#
+ $predicate,
@@ -119,8 +127,8 @@ impl DBCharge {
) -> Result<DBChargeId, DatabaseError> {
sqlx::query!(
r#"
INSERT INTO charges (id, user_id, price_id, amount, currency_code, charge_type, status, due, last_attempt, subscription_id, subscription_interval, payment_platform, payment_platform_id, parent_charge_id, net, tax_amount, tax_platform_id, tax_last_updated, tax_drift_loss)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)
INSERT INTO charges (id, user_id, price_id, amount, currency_code, charge_type, status, due, last_attempt, subscription_id, subscription_interval, payment_platform, payment_platform_id, parent_charge_id, net, tax_amount, tax_platform_id, tax_last_updated, tax_drift_loss, tax_transaction_version, tax_platform_accounting_time)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)
ON CONFLICT (id)
DO UPDATE
SET status = EXCLUDED.status,
@@ -139,7 +147,9 @@ impl DBCharge {
amount = EXCLUDED.amount,
currency_code = EXCLUDED.currency_code,
charge_type = EXCLUDED.charge_type,
tax_drift_loss = EXCLUDED.tax_drift_loss
tax_drift_loss = EXCLUDED.tax_drift_loss,
tax_transaction_version = EXCLUDED.tax_transaction_version,
tax_platform_accounting_time = EXCLUDED.tax_platform_accounting_time
"#,
self.id.0,
self.user_id.0,
@@ -160,6 +170,8 @@ impl DBCharge {
self.tax_platform_id.as_deref(),
self.tax_last_updated,
self.tax_drift_loss,
self.tax_transaction_version,
self.tax_platform_accounting_time,
)
.execute(&mut **transaction)
.await?;

View File

@@ -432,12 +432,17 @@ async fn update_anrok_transactions(
match result {
Ok(response) => {
let should_have_collected = response.tax_amount_to_collect;
let version = response.version.ok_or_else(|| {
ApiError::InvalidInput(
"Anrok response is missing tax transaction version"
.to_owned(),
)
})?;
let drift = should_have_collected - c.tax_amount;
c.tax_drift_loss = Some(drift);
c.tax_drift_loss = Some(response.tax_amount_to_collect);
c.tax_platform_id = Some(tax_platform_id);
c.tax_transaction_version = Some(version);
c.tax_platform_accounting_time = Some(c.due);
c.upsert(txn).await?;
Ok(())
@@ -647,6 +652,8 @@ pub async fn try_process_user_redeemal(
net: None,
tax_last_updated: Some(Utc::now()),
tax_drift_loss: Some(0),
tax_transaction_version: None,
tax_platform_accounting_time: None,
}
.upsert(&mut txn)
.await?;
@@ -1016,7 +1023,7 @@ pub async fn index_subscriptions(
&redis,
&anrok_client,
&stripe_client,
750,
1000,
),
)
.await;

View File

@@ -267,6 +267,24 @@ pub async fn refund_charge(
.tax_identifier
.tax_processor_id;
let Some((
(
original_tax_platform_id,
original_tax_transaction_version,
),
original_tax_platform_accounting_time,
)) = charge
.tax_platform_id
.clone()
.zip(charge.tax_transaction_version)
.zip(charge.tax_platform_accounting_time)
else {
return Err(ApiError::InvalidInput(
"Charge is missing full tax information. Please wait for the original charge to be synchronized with the tax processor."
.to_owned(),
));
};
let refund = stripe::Refund::create(
&stripe_client,
CreateRefund {
@@ -281,13 +299,16 @@ pub async fn refund_charge(
)
.await?;
let anrok_txn_result = anrok_client.create_or_update_txn(
let anrok_txn_result = anrok_client.negate_or_create_partial_negation(
original_tax_platform_id,
original_tax_transaction_version,
charge.amount + charge.tax_amount,
&anrok::Transaction {
id: anrok::transaction_id_stripe_pyr(&refund.id),
fields: anrok::TransactionFields {
customer_address: anrok::Address::from_stripe_address(&billing_address),
currency_code: charge.currency_code.clone(),
accounting_time: Utc::now(),
accounting_time: original_tax_platform_accounting_time,
accounting_time_zone: anrok::AccountingTimeZone::Utc,
line_items: vec![anrok::LineItem::new_including_tax_amount(tax_id, -refund_amount)],
customer_id: Some(format!("stripe:cust:{}", user.stripe_customer_id.unwrap_or_else(|| "unknown".to_owned()))),
@@ -347,6 +368,8 @@ pub async fn refund_charge(
currency_code: charge.currency_code,
tax_last_updated: Some(Utc::now()),
tax_drift_loss: Some(0),
tax_transaction_version: None,
tax_platform_accounting_time: None,
}
.upsert(&mut transaction)
.await?;
@@ -1641,6 +1664,8 @@ pub async fn stripe_webhook(
net: None,
tax_last_updated: Some(Utc::now()),
tax_drift_loss: Some(0),
tax_transaction_version: None,
tax_platform_accounting_time: None,
};
if charge_status != ChargeStatus::Failed {
@@ -2004,6 +2029,8 @@ pub async fn stripe_webhook(
tax_platform_id: None,
tax_last_updated: Some(Utc::now()),
tax_drift_loss: Some(0),
tax_transaction_version: None,
tax_platform_accounting_time: None,
}
.upsert(&mut transaction)
.await?;

View File

@@ -182,6 +182,60 @@ impl Client {
.await
}
pub async fn negate_or_create_partial_negation(
&self,
original_txn_anrok_id: String,
original_txn_version: i32,
original_txn_tax_amount_with_tax: i64,
body: &Transaction,
) -> Result<(), AnrokError> {
let refund_amount = body
.fields
.line_items
.iter()
.map(|l| l.amount_in_smallest_denominations)
.sum::<i64>();
if -refund_amount == original_txn_tax_amount_with_tax {
self.create_full_negation(
original_txn_anrok_id,
original_txn_version,
body.id.clone(),
)
.await?;
} else {
self.create_or_update_txn(body).await?;
}
Ok(())
}
pub async fn create_full_negation(
&self,
original_txn_anrok_id: String,
original_txn_version: i32,
new_txn_id: String,
) -> Result<EmptyResponse, AnrokError> {
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct NegationBody {
original_transaction_id: String,
new_transaction_id: String,
original_transaction_expected_version: i32,
}
self.make_request(
Method::POST,
"/v1/seller/transactions/createNegation",
Some(&NegationBody {
original_transaction_id: original_txn_anrok_id,
new_transaction_id: new_txn_id,
original_transaction_expected_version: original_txn_version,
}),
)
.await
}
pub async fn create_or_update_txn(
&self,
body: &Transaction,