stripe-experiment-sync 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-SNGEJHKN.js +73 -0
- package/dist/index.cjs +76 -41
- package/dist/index.d.cts +22 -8
- package/dist/index.d.ts +22 -8
- package/dist/index.js +68 -99
- package/dist/migrations/0055_bigint_money_columns.sql +72 -0
- package/dist/migrations/0056_sync_run_closed_at.sql +53 -0
- package/dist/supabase/index.cjs +483 -0
- package/dist/supabase/index.d.cts +107 -0
- package/dist/supabase/index.d.ts +107 -0
- package/dist/supabase/index.js +375 -0
- package/package.json +11 -7
package/dist/index.js
CHANGED
|
@@ -1,68 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
version: "1.0.1",
|
|
5
|
-
private: false,
|
|
6
|
-
description: "Stripe Sync Engine to sync Stripe data to Postgres",
|
|
7
|
-
type: "module",
|
|
8
|
-
main: "./dist/index.cjs",
|
|
9
|
-
exports: {
|
|
10
|
-
import: {
|
|
11
|
-
types: "./dist/index.d.ts",
|
|
12
|
-
import: "./dist/index.js"
|
|
13
|
-
},
|
|
14
|
-
require: {
|
|
15
|
-
types: "./dist/index.d.cts",
|
|
16
|
-
require: "./dist/index.cjs"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
scripts: {
|
|
20
|
-
clean: "rimraf dist",
|
|
21
|
-
prebuild: "npm run clean",
|
|
22
|
-
build: "tsup src/index.ts --format esm,cjs --dts --shims && cp -r src/database/migrations dist/migrations",
|
|
23
|
-
lint: "eslint src --ext .ts",
|
|
24
|
-
test: "vitest"
|
|
25
|
-
},
|
|
26
|
-
files: [
|
|
27
|
-
"dist"
|
|
28
|
-
],
|
|
29
|
-
dependencies: {
|
|
30
|
-
pg: "^8.16.3",
|
|
31
|
-
"pg-node-migrations": "0.0.8",
|
|
32
|
-
ws: "^8.18.0",
|
|
33
|
-
yesql: "^7.0.0"
|
|
34
|
-
},
|
|
35
|
-
peerDependencies: {
|
|
36
|
-
stripe: "> 11"
|
|
37
|
-
},
|
|
38
|
-
devDependencies: {
|
|
39
|
-
"@types/node": "^24.10.1",
|
|
40
|
-
"@types/pg": "^8.15.5",
|
|
41
|
-
"@types/ws": "^8.5.13",
|
|
42
|
-
"@types/yesql": "^4.1.4",
|
|
43
|
-
"@vitest/ui": "^4.0.9",
|
|
44
|
-
vitest: "^3.2.4"
|
|
45
|
-
},
|
|
46
|
-
repository: {
|
|
47
|
-
type: "git",
|
|
48
|
-
url: "https://github.com/tx-stripe/stripe-sync-engine.git"
|
|
49
|
-
},
|
|
50
|
-
homepage: "https://github.com/tx-stripe/stripe-sync-engine#readme",
|
|
51
|
-
bugs: {
|
|
52
|
-
url: "https://github.com/tx-stripe/stripe-sync-engine/issues"
|
|
53
|
-
},
|
|
54
|
-
keywords: [
|
|
55
|
-
"stripe",
|
|
56
|
-
"postgres",
|
|
57
|
-
"sync",
|
|
58
|
-
"webhooks",
|
|
59
|
-
"supabase",
|
|
60
|
-
"billing",
|
|
61
|
-
"database",
|
|
62
|
-
"typescript"
|
|
63
|
-
],
|
|
64
|
-
author: "Supabase <https://supabase.com/>"
|
|
65
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
package_default
|
|
3
|
+
} from "./chunk-SNGEJHKN.js";
|
|
66
4
|
|
|
67
5
|
// src/stripeSync.ts
|
|
68
6
|
import Stripe2 from "stripe";
|
|
@@ -398,12 +336,20 @@ var PostgresClient = class {
|
|
|
398
336
|
*/
|
|
399
337
|
async cancelStaleRuns(accountId) {
|
|
400
338
|
await this.query(
|
|
401
|
-
`UPDATE "${this.config.schema}"."
|
|
339
|
+
`UPDATE "${this.config.schema}"."_sync_obj_run" o
|
|
402
340
|
SET status = 'error',
|
|
403
341
|
error_message = 'Auto-cancelled: stale (no update in 5 min)',
|
|
404
342
|
completed_at = now()
|
|
343
|
+
WHERE o."_account_id" = $1
|
|
344
|
+
AND o.status = 'running'
|
|
345
|
+
AND o.updated_at < now() - interval '5 minutes'`,
|
|
346
|
+
[accountId]
|
|
347
|
+
);
|
|
348
|
+
await this.query(
|
|
349
|
+
`UPDATE "${this.config.schema}"."_sync_run" r
|
|
350
|
+
SET closed_at = now()
|
|
405
351
|
WHERE r."_account_id" = $1
|
|
406
|
-
AND r.
|
|
352
|
+
AND r.closed_at IS NULL
|
|
407
353
|
AND EXISTS (
|
|
408
354
|
SELECT 1 FROM "${this.config.schema}"."_sync_obj_run" o
|
|
409
355
|
WHERE o."_account_id" = r."_account_id"
|
|
@@ -413,7 +359,7 @@ var PostgresClient = class {
|
|
|
413
359
|
SELECT 1 FROM "${this.config.schema}"."_sync_obj_run" o
|
|
414
360
|
WHERE o."_account_id" = r."_account_id"
|
|
415
361
|
AND o.run_started_at = r.started_at
|
|
416
|
-
AND o.
|
|
362
|
+
AND o.status IN ('pending', 'running')
|
|
417
363
|
)`,
|
|
418
364
|
[accountId]
|
|
419
365
|
);
|
|
@@ -429,7 +375,7 @@ var PostgresClient = class {
|
|
|
429
375
|
await this.cancelStaleRuns(accountId);
|
|
430
376
|
const existing = await this.query(
|
|
431
377
|
`SELECT "_account_id", started_at FROM "${this.config.schema}"."_sync_run"
|
|
432
|
-
WHERE "_account_id" = $1 AND
|
|
378
|
+
WHERE "_account_id" = $1 AND closed_at IS NULL`,
|
|
433
379
|
[accountId]
|
|
434
380
|
);
|
|
435
381
|
if (existing.rows.length > 0) {
|
|
@@ -458,7 +404,7 @@ var PostgresClient = class {
|
|
|
458
404
|
async getActiveSyncRun(accountId) {
|
|
459
405
|
const result = await this.query(
|
|
460
406
|
`SELECT "_account_id", started_at FROM "${this.config.schema}"."_sync_run"
|
|
461
|
-
WHERE "_account_id" = $1 AND
|
|
407
|
+
WHERE "_account_id" = $1 AND closed_at IS NULL`,
|
|
462
408
|
[accountId]
|
|
463
409
|
);
|
|
464
410
|
if (result.rows.length === 0) return null;
|
|
@@ -466,11 +412,12 @@ var PostgresClient = class {
|
|
|
466
412
|
return { accountId: row._account_id, runStartedAt: row.started_at };
|
|
467
413
|
}
|
|
468
414
|
/**
|
|
469
|
-
* Get
|
|
415
|
+
* Get sync run config (for concurrency control).
|
|
416
|
+
* Status is derived from sync_dashboard view.
|
|
470
417
|
*/
|
|
471
418
|
async getSyncRun(accountId, runStartedAt) {
|
|
472
419
|
const result = await this.query(
|
|
473
|
-
`SELECT "_account_id", started_at,
|
|
420
|
+
`SELECT "_account_id", started_at, max_concurrent, closed_at
|
|
474
421
|
FROM "${this.config.schema}"."_sync_run"
|
|
475
422
|
WHERE "_account_id" = $1 AND started_at = $2`,
|
|
476
423
|
[accountId, runStartedAt]
|
|
@@ -480,32 +427,22 @@ var PostgresClient = class {
|
|
|
480
427
|
return {
|
|
481
428
|
accountId: row._account_id,
|
|
482
429
|
runStartedAt: row.started_at,
|
|
483
|
-
|
|
484
|
-
|
|
430
|
+
maxConcurrent: row.max_concurrent,
|
|
431
|
+
closedAt: row.closed_at
|
|
485
432
|
};
|
|
486
433
|
}
|
|
487
434
|
/**
|
|
488
|
-
*
|
|
435
|
+
* Close a sync run (mark as done).
|
|
436
|
+
* Status (complete/error) is derived from object run states.
|
|
489
437
|
*/
|
|
490
|
-
async
|
|
438
|
+
async closeSyncRun(accountId, runStartedAt) {
|
|
491
439
|
await this.query(
|
|
492
440
|
`UPDATE "${this.config.schema}"."_sync_run"
|
|
493
|
-
SET
|
|
494
|
-
WHERE "_account_id" = $1 AND started_at = $2`,
|
|
441
|
+
SET closed_at = now()
|
|
442
|
+
WHERE "_account_id" = $1 AND started_at = $2 AND closed_at IS NULL`,
|
|
495
443
|
[accountId, runStartedAt]
|
|
496
444
|
);
|
|
497
445
|
}
|
|
498
|
-
/**
|
|
499
|
-
* Mark a sync run as failed.
|
|
500
|
-
*/
|
|
501
|
-
async failSyncRun(accountId, runStartedAt, errorMessage) {
|
|
502
|
-
await this.query(
|
|
503
|
-
`UPDATE "${this.config.schema}"."_sync_run"
|
|
504
|
-
SET status = 'error', error_message = $3, completed_at = now()
|
|
505
|
-
WHERE "_account_id" = $1 AND started_at = $2`,
|
|
506
|
-
[accountId, runStartedAt, errorMessage]
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
446
|
/**
|
|
510
447
|
* Create object run entries for a sync run.
|
|
511
448
|
* All objects start as 'pending'.
|
|
@@ -629,6 +566,7 @@ var PostgresClient = class {
|
|
|
629
566
|
}
|
|
630
567
|
/**
|
|
631
568
|
* Mark an object sync as complete.
|
|
569
|
+
* Auto-closes the run when all objects are done.
|
|
632
570
|
*/
|
|
633
571
|
async completeObjectSync(accountId, runStartedAt, object) {
|
|
634
572
|
await this.query(
|
|
@@ -637,9 +575,14 @@ var PostgresClient = class {
|
|
|
637
575
|
WHERE "_account_id" = $1 AND run_started_at = $2 AND object = $3`,
|
|
638
576
|
[accountId, runStartedAt, object]
|
|
639
577
|
);
|
|
578
|
+
const allDone = await this.areAllObjectsComplete(accountId, runStartedAt);
|
|
579
|
+
if (allDone) {
|
|
580
|
+
await this.closeSyncRun(accountId, runStartedAt);
|
|
581
|
+
}
|
|
640
582
|
}
|
|
641
583
|
/**
|
|
642
584
|
* Mark an object sync as failed.
|
|
585
|
+
* Auto-closes the run when all objects are done.
|
|
643
586
|
*/
|
|
644
587
|
async failObjectSync(accountId, runStartedAt, object, errorMessage) {
|
|
645
588
|
await this.query(
|
|
@@ -648,6 +591,21 @@ var PostgresClient = class {
|
|
|
648
591
|
WHERE "_account_id" = $1 AND run_started_at = $2 AND object = $3`,
|
|
649
592
|
[accountId, runStartedAt, object, errorMessage]
|
|
650
593
|
);
|
|
594
|
+
const allDone = await this.areAllObjectsComplete(accountId, runStartedAt);
|
|
595
|
+
if (allDone) {
|
|
596
|
+
await this.closeSyncRun(accountId, runStartedAt);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Check if any object in a run has errored.
|
|
601
|
+
*/
|
|
602
|
+
async hasAnyObjectErrors(accountId, runStartedAt) {
|
|
603
|
+
const result = await this.query(
|
|
604
|
+
`SELECT COUNT(*) as count FROM "${this.config.schema}"."_sync_obj_run"
|
|
605
|
+
WHERE "_account_id" = $1 AND run_started_at = $2 AND status = 'error'`,
|
|
606
|
+
[accountId, runStartedAt]
|
|
607
|
+
);
|
|
608
|
+
return parseInt(result.rows[0].count) > 0;
|
|
651
609
|
}
|
|
652
610
|
/**
|
|
653
611
|
* Count running objects in a run.
|
|
@@ -689,6 +647,13 @@ var PostgresClient = class {
|
|
|
689
647
|
);
|
|
690
648
|
return parseInt(result.rows[0].count) === 0;
|
|
691
649
|
}
|
|
650
|
+
/**
|
|
651
|
+
* Closes the database connection pool and cleans up resources.
|
|
652
|
+
* Call this when you're done using the PostgresClient instance.
|
|
653
|
+
*/
|
|
654
|
+
async close() {
|
|
655
|
+
await this.pool.end();
|
|
656
|
+
}
|
|
692
657
|
};
|
|
693
658
|
|
|
694
659
|
// src/schemas/managed_webhook.ts
|
|
@@ -1243,8 +1208,8 @@ var StripeSync = class {
|
|
|
1243
1208
|
// Depends on invoice
|
|
1244
1209
|
listFn: (p) => this.stripe.creditNotes.list(p),
|
|
1245
1210
|
upsertFn: (items, id, bf) => this.upsertCreditNotes(items, id, bf),
|
|
1246
|
-
supportsCreatedFilter:
|
|
1247
|
-
// credit_notes
|
|
1211
|
+
supportsCreatedFilter: true
|
|
1212
|
+
// credit_notes support created filter
|
|
1248
1213
|
},
|
|
1249
1214
|
dispute: {
|
|
1250
1215
|
order: 14,
|
|
@@ -1897,14 +1862,10 @@ var StripeSync = class {
|
|
|
1897
1862
|
}
|
|
1898
1863
|
}
|
|
1899
1864
|
}
|
|
1900
|
-
await this.postgresClient.
|
|
1865
|
+
await this.postgresClient.closeSyncRun(accountId, runStartedAt);
|
|
1901
1866
|
return results;
|
|
1902
1867
|
} catch (error) {
|
|
1903
|
-
await this.postgresClient.
|
|
1904
|
-
accountId,
|
|
1905
|
-
runStartedAt,
|
|
1906
|
-
error instanceof Error ? error.message : "Unknown error"
|
|
1907
|
-
);
|
|
1868
|
+
await this.postgresClient.closeSyncRun(accountId, runStartedAt);
|
|
1908
1869
|
throw error;
|
|
1909
1870
|
}
|
|
1910
1871
|
}
|
|
@@ -2435,12 +2396,13 @@ var StripeSync = class {
|
|
|
2435
2396
|
await this.postgresClient.tryStartObjectSync(accountId, runStartedAt, resourceName);
|
|
2436
2397
|
try {
|
|
2437
2398
|
const result = await fn(cursor, runStartedAt);
|
|
2438
|
-
await this.postgresClient.
|
|
2399
|
+
await this.postgresClient.completeObjectSync(accountId, runStartedAt, resourceName);
|
|
2439
2400
|
return result;
|
|
2440
2401
|
} catch (error) {
|
|
2441
|
-
await this.postgresClient.
|
|
2402
|
+
await this.postgresClient.failObjectSync(
|
|
2442
2403
|
accountId,
|
|
2443
2404
|
runStartedAt,
|
|
2405
|
+
resourceName,
|
|
2444
2406
|
error instanceof Error ? error.message : "Unknown error"
|
|
2445
2407
|
);
|
|
2446
2408
|
throw error;
|
|
@@ -3188,6 +3150,13 @@ var StripeSync = class {
|
|
|
3188
3150
|
}
|
|
3189
3151
|
return entities;
|
|
3190
3152
|
}
|
|
3153
|
+
/**
|
|
3154
|
+
* Closes the database connection pool and cleans up resources.
|
|
3155
|
+
* Call this when you're done using the StripeSync instance.
|
|
3156
|
+
*/
|
|
3157
|
+
async close() {
|
|
3158
|
+
await this.postgresClient.pool.end();
|
|
3159
|
+
}
|
|
3191
3160
|
};
|
|
3192
3161
|
function chunkArray(array, chunkSize) {
|
|
3193
3162
|
const result = [];
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
-- Fix generated columns: must drop and recreate with ::bigint cast
|
|
2
|
+
-- Money columns that can overflow PostgreSQL integer max (~2.1 billion)
|
|
3
|
+
|
|
4
|
+
-- checkout_session_line_items
|
|
5
|
+
ALTER TABLE "stripe"."checkout_session_line_items" DROP COLUMN "amount_discount";
|
|
6
|
+
ALTER TABLE "stripe"."checkout_session_line_items" ADD COLUMN "amount_discount" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_discount')::bigint) STORED;
|
|
7
|
+
ALTER TABLE "stripe"."checkout_session_line_items" DROP COLUMN "amount_subtotal";
|
|
8
|
+
ALTER TABLE "stripe"."checkout_session_line_items" ADD COLUMN "amount_subtotal" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_subtotal')::bigint) STORED;
|
|
9
|
+
ALTER TABLE "stripe"."checkout_session_line_items" DROP COLUMN "amount_tax";
|
|
10
|
+
ALTER TABLE "stripe"."checkout_session_line_items" ADD COLUMN "amount_tax" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_tax')::bigint) STORED;
|
|
11
|
+
ALTER TABLE "stripe"."checkout_session_line_items" DROP COLUMN "amount_total";
|
|
12
|
+
ALTER TABLE "stripe"."checkout_session_line_items" ADD COLUMN "amount_total" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_total')::bigint) STORED;
|
|
13
|
+
|
|
14
|
+
-- checkout_sessions
|
|
15
|
+
ALTER TABLE "stripe"."checkout_sessions" DROP COLUMN "amount_subtotal";
|
|
16
|
+
ALTER TABLE "stripe"."checkout_sessions" ADD COLUMN "amount_subtotal" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_subtotal')::bigint) STORED;
|
|
17
|
+
ALTER TABLE "stripe"."checkout_sessions" DROP COLUMN "amount_total";
|
|
18
|
+
ALTER TABLE "stripe"."checkout_sessions" ADD COLUMN "amount_total" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_total')::bigint) STORED;
|
|
19
|
+
|
|
20
|
+
-- credit_notes
|
|
21
|
+
ALTER TABLE "stripe"."credit_notes" DROP COLUMN "amount";
|
|
22
|
+
ALTER TABLE "stripe"."credit_notes" ADD COLUMN "amount" bigint GENERATED ALWAYS AS ((_raw_data->>'amount')::bigint) STORED;
|
|
23
|
+
ALTER TABLE "stripe"."credit_notes" DROP COLUMN "amount_shipping";
|
|
24
|
+
ALTER TABLE "stripe"."credit_notes" ADD COLUMN "amount_shipping" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_shipping')::bigint) STORED;
|
|
25
|
+
ALTER TABLE "stripe"."credit_notes" DROP COLUMN "discount_amount";
|
|
26
|
+
ALTER TABLE "stripe"."credit_notes" ADD COLUMN "discount_amount" bigint GENERATED ALWAYS AS ((_raw_data->>'discount_amount')::bigint) STORED;
|
|
27
|
+
ALTER TABLE "stripe"."credit_notes" DROP COLUMN "out_of_band_amount";
|
|
28
|
+
ALTER TABLE "stripe"."credit_notes" ADD COLUMN "out_of_band_amount" bigint GENERATED ALWAYS AS ((_raw_data->>'out_of_band_amount')::bigint) STORED;
|
|
29
|
+
ALTER TABLE "stripe"."credit_notes" DROP COLUMN "subtotal";
|
|
30
|
+
ALTER TABLE "stripe"."credit_notes" ADD COLUMN "subtotal" bigint GENERATED ALWAYS AS ((_raw_data->>'subtotal')::bigint) STORED;
|
|
31
|
+
ALTER TABLE "stripe"."credit_notes" DROP COLUMN "subtotal_excluding_tax";
|
|
32
|
+
ALTER TABLE "stripe"."credit_notes" ADD COLUMN "subtotal_excluding_tax" bigint GENERATED ALWAYS AS ((_raw_data->>'subtotal_excluding_tax')::bigint) STORED;
|
|
33
|
+
ALTER TABLE "stripe"."credit_notes" DROP COLUMN "total";
|
|
34
|
+
ALTER TABLE "stripe"."credit_notes" ADD COLUMN "total" bigint GENERATED ALWAYS AS ((_raw_data->>'total')::bigint) STORED;
|
|
35
|
+
ALTER TABLE "stripe"."credit_notes" DROP COLUMN "total_excluding_tax";
|
|
36
|
+
ALTER TABLE "stripe"."credit_notes" ADD COLUMN "total_excluding_tax" bigint GENERATED ALWAYS AS ((_raw_data->>'total_excluding_tax')::bigint) STORED;
|
|
37
|
+
|
|
38
|
+
-- customers
|
|
39
|
+
ALTER TABLE "stripe"."customers" DROP COLUMN "balance";
|
|
40
|
+
ALTER TABLE "stripe"."customers" ADD COLUMN "balance" bigint GENERATED ALWAYS AS ((_raw_data->>'balance')::bigint) STORED;
|
|
41
|
+
|
|
42
|
+
-- invoices
|
|
43
|
+
ALTER TABLE "stripe"."invoices" DROP COLUMN "ending_balance";
|
|
44
|
+
ALTER TABLE "stripe"."invoices" ADD COLUMN "ending_balance" bigint GENERATED ALWAYS AS ((_raw_data->>'ending_balance')::bigint) STORED;
|
|
45
|
+
ALTER TABLE "stripe"."invoices" DROP COLUMN "starting_balance";
|
|
46
|
+
ALTER TABLE "stripe"."invoices" ADD COLUMN "starting_balance" bigint GENERATED ALWAYS AS ((_raw_data->>'starting_balance')::bigint) STORED;
|
|
47
|
+
ALTER TABLE "stripe"."invoices" DROP COLUMN "subtotal";
|
|
48
|
+
ALTER TABLE "stripe"."invoices" ADD COLUMN "subtotal" bigint GENERATED ALWAYS AS ((_raw_data->>'subtotal')::bigint) STORED;
|
|
49
|
+
ALTER TABLE "stripe"."invoices" DROP COLUMN "tax";
|
|
50
|
+
ALTER TABLE "stripe"."invoices" ADD COLUMN "tax" bigint GENERATED ALWAYS AS ((_raw_data->>'tax')::bigint) STORED;
|
|
51
|
+
ALTER TABLE "stripe"."invoices" DROP COLUMN "post_payment_credit_notes_amount";
|
|
52
|
+
ALTER TABLE "stripe"."invoices" ADD COLUMN "post_payment_credit_notes_amount" bigint GENERATED ALWAYS AS ((_raw_data->>'post_payment_credit_notes_amount')::bigint) STORED;
|
|
53
|
+
ALTER TABLE "stripe"."invoices" DROP COLUMN "pre_payment_credit_notes_amount";
|
|
54
|
+
ALTER TABLE "stripe"."invoices" ADD COLUMN "pre_payment_credit_notes_amount" bigint GENERATED ALWAYS AS ((_raw_data->>'pre_payment_credit_notes_amount')::bigint) STORED;
|
|
55
|
+
|
|
56
|
+
-- payment_intents
|
|
57
|
+
ALTER TABLE "stripe"."payment_intents" DROP COLUMN "amount";
|
|
58
|
+
ALTER TABLE "stripe"."payment_intents" ADD COLUMN "amount" bigint GENERATED ALWAYS AS ((_raw_data->>'amount')::bigint) STORED;
|
|
59
|
+
ALTER TABLE "stripe"."payment_intents" DROP COLUMN "amount_capturable";
|
|
60
|
+
ALTER TABLE "stripe"."payment_intents" ADD COLUMN "amount_capturable" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_capturable')::bigint) STORED;
|
|
61
|
+
ALTER TABLE "stripe"."payment_intents" DROP COLUMN "amount_received";
|
|
62
|
+
ALTER TABLE "stripe"."payment_intents" ADD COLUMN "amount_received" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_received')::bigint) STORED;
|
|
63
|
+
ALTER TABLE "stripe"."payment_intents" DROP COLUMN "application_fee_amount";
|
|
64
|
+
ALTER TABLE "stripe"."payment_intents" ADD COLUMN "application_fee_amount" bigint GENERATED ALWAYS AS ((_raw_data->>'application_fee_amount')::bigint) STORED;
|
|
65
|
+
|
|
66
|
+
-- prices
|
|
67
|
+
ALTER TABLE "stripe"."prices" DROP COLUMN "unit_amount";
|
|
68
|
+
ALTER TABLE "stripe"."prices" ADD COLUMN "unit_amount" bigint GENERATED ALWAYS AS ((_raw_data->>'unit_amount')::bigint) STORED;
|
|
69
|
+
|
|
70
|
+
-- refunds
|
|
71
|
+
ALTER TABLE "stripe"."refunds" DROP COLUMN "amount";
|
|
72
|
+
ALTER TABLE "stripe"."refunds" ADD COLUMN "amount" bigint GENERATED ALWAYS AS ((_raw_data->>'amount')::bigint) STORED;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
-- Add closed_at column to _sync_run
|
|
2
|
+
-- closed_at IS NULL means the run is still active
|
|
3
|
+
-- Status is derived from object states when closed_at IS NOT NULL
|
|
4
|
+
|
|
5
|
+
-- Step 1: Drop dependent view first
|
|
6
|
+
DROP VIEW IF EXISTS "stripe"."sync_dashboard";
|
|
7
|
+
|
|
8
|
+
-- Step 2: Drop the old constraint, status column, and completed_at column
|
|
9
|
+
ALTER TABLE "stripe"."_sync_run" DROP CONSTRAINT IF EXISTS one_active_run_per_account;
|
|
10
|
+
ALTER TABLE "stripe"."_sync_run" DROP COLUMN IF EXISTS status;
|
|
11
|
+
ALTER TABLE "stripe"."_sync_run" DROP COLUMN IF EXISTS completed_at;
|
|
12
|
+
|
|
13
|
+
-- Step 3: Add closed_at column
|
|
14
|
+
ALTER TABLE "stripe"."_sync_run" ADD COLUMN IF NOT EXISTS closed_at TIMESTAMPTZ;
|
|
15
|
+
|
|
16
|
+
-- Step 4: Create exclusion constraint (only one active run per account)
|
|
17
|
+
ALTER TABLE "stripe"."_sync_run"
|
|
18
|
+
ADD CONSTRAINT one_active_run_per_account
|
|
19
|
+
EXCLUDE ("_account_id" WITH =) WHERE (closed_at IS NULL);
|
|
20
|
+
|
|
21
|
+
-- Step 5: Recreate sync_dashboard view (run-level only, one row per run)
|
|
22
|
+
-- Base table: _sync_run (parent sync operation)
|
|
23
|
+
-- Child table: _sync_obj_run (individual object syncs)
|
|
24
|
+
CREATE OR REPLACE VIEW "stripe"."sync_dashboard" AS
|
|
25
|
+
SELECT
|
|
26
|
+
run."_account_id" as account_id,
|
|
27
|
+
run.started_at,
|
|
28
|
+
run.closed_at,
|
|
29
|
+
run.max_concurrent,
|
|
30
|
+
run.triggered_by,
|
|
31
|
+
run.updated_at,
|
|
32
|
+
-- Derived status from object states
|
|
33
|
+
CASE
|
|
34
|
+
WHEN run.closed_at IS NULL THEN 'running'
|
|
35
|
+
WHEN EXISTS (
|
|
36
|
+
SELECT 1 FROM "stripe"."_sync_obj_run" obj
|
|
37
|
+
WHERE obj."_account_id" = run."_account_id"
|
|
38
|
+
AND obj.run_started_at = run.started_at
|
|
39
|
+
AND obj.status = 'error'
|
|
40
|
+
) THEN 'error'
|
|
41
|
+
ELSE 'complete'
|
|
42
|
+
END as status,
|
|
43
|
+
-- First error message from failed objects
|
|
44
|
+
(SELECT obj.error_message FROM "stripe"."_sync_obj_run" obj
|
|
45
|
+
WHERE obj."_account_id" = run."_account_id"
|
|
46
|
+
AND obj.run_started_at = run.started_at
|
|
47
|
+
AND obj.status = 'error'
|
|
48
|
+
ORDER BY obj.object LIMIT 1) as error_message,
|
|
49
|
+
-- Total processed count across all objects
|
|
50
|
+
COALESCE((SELECT SUM(obj.processed_count) FROM "stripe"."_sync_obj_run" obj
|
|
51
|
+
WHERE obj."_account_id" = run."_account_id"
|
|
52
|
+
AND obj.run_started_at = run.started_at), 0) as processed_count
|
|
53
|
+
FROM "stripe"."_sync_run" run;
|