@wopr-network/platform-core 1.42.3 → 1.44.0
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/.github/workflows/key-server-image.yml +35 -0
- package/Dockerfile.key-server +20 -0
- package/GATEWAY_BILLING_RESEARCH.md +430 -0
- package/biome.json +2 -9
- package/dist/billing/crypto/__tests__/key-server.test.js +240 -0
- package/dist/billing/crypto/btc/watcher.d.ts +2 -0
- package/dist/billing/crypto/btc/watcher.js +1 -1
- package/dist/billing/crypto/charge-store.d.ts +7 -1
- package/dist/billing/crypto/charge-store.js +7 -1
- package/dist/billing/crypto/client.d.ts +68 -30
- package/dist/billing/crypto/client.js +63 -46
- package/dist/billing/crypto/client.test.js +66 -83
- package/dist/billing/crypto/index.d.ts +8 -8
- package/dist/billing/crypto/index.js +4 -5
- package/dist/billing/crypto/key-server-entry.js +84 -0
- package/dist/billing/crypto/key-server-webhook.d.ts +33 -0
- package/dist/billing/crypto/key-server-webhook.js +73 -0
- package/dist/billing/crypto/key-server.d.ts +20 -0
- package/dist/billing/crypto/key-server.js +263 -0
- package/dist/billing/crypto/watcher-service.d.ts +33 -0
- package/dist/billing/crypto/watcher-service.js +295 -0
- package/dist/billing/index.js +1 -1
- package/dist/db/schema/crypto.d.ts +464 -2
- package/dist/db/schema/crypto.js +60 -6
- package/dist/monetization/crypto/__tests__/webhook.test.js +57 -92
- package/dist/monetization/crypto/index.d.ts +4 -4
- package/dist/monetization/crypto/index.js +2 -2
- package/dist/monetization/crypto/webhook.d.ts +13 -14
- package/dist/monetization/crypto/webhook.js +12 -83
- package/dist/monetization/index.d.ts +2 -2
- package/dist/monetization/index.js +1 -1
- package/drizzle/migrations/0014_crypto_key_server.sql +60 -0
- package/drizzle/migrations/0015_callback_url.sql +32 -0
- package/drizzle/migrations/meta/_journal.json +28 -0
- package/package.json +2 -1
- package/src/billing/crypto/__tests__/key-server.test.ts +262 -0
- package/src/billing/crypto/btc/watcher.ts +3 -1
- package/src/billing/crypto/charge-store.ts +13 -1
- package/src/billing/crypto/client.test.ts +70 -98
- package/src/billing/crypto/client.ts +118 -59
- package/src/billing/crypto/index.ts +19 -14
- package/src/billing/crypto/key-server-entry.ts +96 -0
- package/src/billing/crypto/key-server-webhook.ts +119 -0
- package/src/billing/crypto/key-server.ts +343 -0
- package/src/billing/crypto/watcher-service.ts +381 -0
- package/src/billing/index.ts +1 -1
- package/src/db/schema/crypto.ts +75 -6
- package/src/monetization/crypto/__tests__/webhook.test.ts +61 -104
- package/src/monetization/crypto/index.ts +9 -11
- package/src/monetization/crypto/webhook.ts +25 -99
- package/src/monetization/index.ts +3 -7
- package/dist/billing/crypto/checkout.d.ts +0 -18
- package/dist/billing/crypto/checkout.js +0 -35
- package/dist/billing/crypto/checkout.test.js +0 -71
- package/dist/billing/crypto/webhook.d.ts +0 -34
- package/dist/billing/crypto/webhook.js +0 -107
- package/dist/billing/crypto/webhook.test.js +0 -266
- package/src/billing/crypto/checkout.test.ts +0 -93
- package/src/billing/crypto/checkout.ts +0 -48
- package/src/billing/crypto/webhook.test.ts +0 -340
- package/src/billing/crypto/webhook.ts +0 -136
- /package/dist/billing/crypto/{checkout.test.d.ts → __tests__/key-server.test.d.ts} +0 -0
- /package/dist/billing/crypto/{webhook.test.d.ts → key-server-entry.d.ts} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Crypto payment charges — tracks the lifecycle of each
|
|
3
|
-
* reference_id is the
|
|
2
|
+
* Crypto payment charges — tracks the lifecycle of each payment.
|
|
3
|
+
* reference_id is the charge ID (e.g. "btc:bc1q...").
|
|
4
4
|
*
|
|
5
5
|
* amountUsdCents stores the requested amount in USD cents (integer).
|
|
6
6
|
* This is NOT nanodollars — Credit.fromCents() handles the conversion
|
|
@@ -231,6 +231,57 @@ export declare const cryptoCharges: import("drizzle-orm/pg-core").PgTableWithCol
|
|
|
231
231
|
identity: undefined;
|
|
232
232
|
generated: undefined;
|
|
233
233
|
}, {}, {}>;
|
|
234
|
+
callbackUrl: import("drizzle-orm/pg-core").PgColumn<{
|
|
235
|
+
name: "callback_url";
|
|
236
|
+
tableName: "crypto_charges";
|
|
237
|
+
dataType: "string";
|
|
238
|
+
columnType: "PgText";
|
|
239
|
+
data: string;
|
|
240
|
+
driverParam: string;
|
|
241
|
+
notNull: false;
|
|
242
|
+
hasDefault: false;
|
|
243
|
+
isPrimaryKey: false;
|
|
244
|
+
isAutoincrement: false;
|
|
245
|
+
hasRuntimeDefault: false;
|
|
246
|
+
enumValues: [string, ...string[]];
|
|
247
|
+
baseColumn: never;
|
|
248
|
+
identity: undefined;
|
|
249
|
+
generated: undefined;
|
|
250
|
+
}, {}, {}>;
|
|
251
|
+
expectedAmount: import("drizzle-orm/pg-core").PgColumn<{
|
|
252
|
+
name: "expected_amount";
|
|
253
|
+
tableName: "crypto_charges";
|
|
254
|
+
dataType: "string";
|
|
255
|
+
columnType: "PgText";
|
|
256
|
+
data: string;
|
|
257
|
+
driverParam: string;
|
|
258
|
+
notNull: false;
|
|
259
|
+
hasDefault: false;
|
|
260
|
+
isPrimaryKey: false;
|
|
261
|
+
isAutoincrement: false;
|
|
262
|
+
hasRuntimeDefault: false;
|
|
263
|
+
enumValues: [string, ...string[]];
|
|
264
|
+
baseColumn: never;
|
|
265
|
+
identity: undefined;
|
|
266
|
+
generated: undefined;
|
|
267
|
+
}, {}, {}>;
|
|
268
|
+
receivedAmount: import("drizzle-orm/pg-core").PgColumn<{
|
|
269
|
+
name: "received_amount";
|
|
270
|
+
tableName: "crypto_charges";
|
|
271
|
+
dataType: "string";
|
|
272
|
+
columnType: "PgText";
|
|
273
|
+
data: string;
|
|
274
|
+
driverParam: string;
|
|
275
|
+
notNull: false;
|
|
276
|
+
hasDefault: false;
|
|
277
|
+
isPrimaryKey: false;
|
|
278
|
+
isAutoincrement: false;
|
|
279
|
+
hasRuntimeDefault: false;
|
|
280
|
+
enumValues: [string, ...string[]];
|
|
281
|
+
baseColumn: never;
|
|
282
|
+
identity: undefined;
|
|
283
|
+
generated: undefined;
|
|
284
|
+
}, {}, {}>;
|
|
234
285
|
};
|
|
235
286
|
dialect: "pg";
|
|
236
287
|
}>;
|
|
@@ -300,6 +351,9 @@ export declare const watcherCursors: import("drizzle-orm/pg-core").PgTableWithCo
|
|
|
300
351
|
* Payment method registry — runtime-configurable tokens/chains.
|
|
301
352
|
* Admin inserts a row to enable a new payment method. No deploy needed.
|
|
302
353
|
* Contract addresses are immutable on-chain but configurable here.
|
|
354
|
+
*
|
|
355
|
+
* nextIndex is an atomic counter for HD derivation — never reuses an index.
|
|
356
|
+
* Increment via UPDATE ... SET next_index = next_index + 1 RETURNING next_index.
|
|
303
357
|
*/
|
|
304
358
|
export declare const paymentMethods: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
305
359
|
name: "payment_methods";
|
|
@@ -373,6 +427,23 @@ export declare const paymentMethods: import("drizzle-orm/pg-core").PgTableWithCo
|
|
|
373
427
|
identity: undefined;
|
|
374
428
|
generated: undefined;
|
|
375
429
|
}, {}, {}>;
|
|
430
|
+
network: import("drizzle-orm/pg-core").PgColumn<{
|
|
431
|
+
name: "network";
|
|
432
|
+
tableName: "payment_methods";
|
|
433
|
+
dataType: "string";
|
|
434
|
+
columnType: "PgText";
|
|
435
|
+
data: string;
|
|
436
|
+
driverParam: string;
|
|
437
|
+
notNull: true;
|
|
438
|
+
hasDefault: true;
|
|
439
|
+
isPrimaryKey: false;
|
|
440
|
+
isAutoincrement: false;
|
|
441
|
+
hasRuntimeDefault: false;
|
|
442
|
+
enumValues: [string, ...string[]];
|
|
443
|
+
baseColumn: never;
|
|
444
|
+
identity: undefined;
|
|
445
|
+
generated: undefined;
|
|
446
|
+
}, {}, {}>;
|
|
376
447
|
contractAddress: import("drizzle-orm/pg-core").PgColumn<{
|
|
377
448
|
name: "contract_address";
|
|
378
449
|
tableName: "payment_methods";
|
|
@@ -526,6 +597,23 @@ export declare const paymentMethods: import("drizzle-orm/pg-core").PgTableWithCo
|
|
|
526
597
|
identity: undefined;
|
|
527
598
|
generated: undefined;
|
|
528
599
|
}, {}, {}>;
|
|
600
|
+
nextIndex: import("drizzle-orm/pg-core").PgColumn<{
|
|
601
|
+
name: "next_index";
|
|
602
|
+
tableName: "payment_methods";
|
|
603
|
+
dataType: "number";
|
|
604
|
+
columnType: "PgInteger";
|
|
605
|
+
data: number;
|
|
606
|
+
driverParam: string | number;
|
|
607
|
+
notNull: true;
|
|
608
|
+
hasDefault: true;
|
|
609
|
+
isPrimaryKey: false;
|
|
610
|
+
isAutoincrement: false;
|
|
611
|
+
hasRuntimeDefault: false;
|
|
612
|
+
enumValues: undefined;
|
|
613
|
+
baseColumn: never;
|
|
614
|
+
identity: undefined;
|
|
615
|
+
generated: undefined;
|
|
616
|
+
}, {}, {}>;
|
|
529
617
|
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
530
618
|
name: "created_at";
|
|
531
619
|
tableName: "payment_methods";
|
|
@@ -546,6 +634,380 @@ export declare const paymentMethods: import("drizzle-orm/pg-core").PgTableWithCo
|
|
|
546
634
|
};
|
|
547
635
|
dialect: "pg";
|
|
548
636
|
}>;
|
|
637
|
+
/**
|
|
638
|
+
* BIP-44 path allocation registry — tracks which derivation paths are in use.
|
|
639
|
+
* The server knows which paths are allocated so you never collide.
|
|
640
|
+
* The seed phrase never touches the server — only xpubs.
|
|
641
|
+
*/
|
|
642
|
+
export declare const pathAllocations: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
643
|
+
name: "path_allocations";
|
|
644
|
+
schema: undefined;
|
|
645
|
+
columns: {
|
|
646
|
+
coinType: import("drizzle-orm/pg-core").PgColumn<{
|
|
647
|
+
name: "coin_type";
|
|
648
|
+
tableName: "path_allocations";
|
|
649
|
+
dataType: "number";
|
|
650
|
+
columnType: "PgInteger";
|
|
651
|
+
data: number;
|
|
652
|
+
driverParam: string | number;
|
|
653
|
+
notNull: true;
|
|
654
|
+
hasDefault: false;
|
|
655
|
+
isPrimaryKey: false;
|
|
656
|
+
isAutoincrement: false;
|
|
657
|
+
hasRuntimeDefault: false;
|
|
658
|
+
enumValues: undefined;
|
|
659
|
+
baseColumn: never;
|
|
660
|
+
identity: undefined;
|
|
661
|
+
generated: undefined;
|
|
662
|
+
}, {}, {}>;
|
|
663
|
+
accountIndex: import("drizzle-orm/pg-core").PgColumn<{
|
|
664
|
+
name: "account_index";
|
|
665
|
+
tableName: "path_allocations";
|
|
666
|
+
dataType: "number";
|
|
667
|
+
columnType: "PgInteger";
|
|
668
|
+
data: number;
|
|
669
|
+
driverParam: string | number;
|
|
670
|
+
notNull: true;
|
|
671
|
+
hasDefault: false;
|
|
672
|
+
isPrimaryKey: false;
|
|
673
|
+
isAutoincrement: false;
|
|
674
|
+
hasRuntimeDefault: false;
|
|
675
|
+
enumValues: undefined;
|
|
676
|
+
baseColumn: never;
|
|
677
|
+
identity: undefined;
|
|
678
|
+
generated: undefined;
|
|
679
|
+
}, {}, {}>;
|
|
680
|
+
chainId: import("drizzle-orm/pg-core").PgColumn<{
|
|
681
|
+
name: "chain_id";
|
|
682
|
+
tableName: "path_allocations";
|
|
683
|
+
dataType: "string";
|
|
684
|
+
columnType: "PgText";
|
|
685
|
+
data: string;
|
|
686
|
+
driverParam: string;
|
|
687
|
+
notNull: false;
|
|
688
|
+
hasDefault: false;
|
|
689
|
+
isPrimaryKey: false;
|
|
690
|
+
isAutoincrement: false;
|
|
691
|
+
hasRuntimeDefault: false;
|
|
692
|
+
enumValues: [string, ...string[]];
|
|
693
|
+
baseColumn: never;
|
|
694
|
+
identity: undefined;
|
|
695
|
+
generated: undefined;
|
|
696
|
+
}, {}, {}>;
|
|
697
|
+
xpub: import("drizzle-orm/pg-core").PgColumn<{
|
|
698
|
+
name: "xpub";
|
|
699
|
+
tableName: "path_allocations";
|
|
700
|
+
dataType: "string";
|
|
701
|
+
columnType: "PgText";
|
|
702
|
+
data: string;
|
|
703
|
+
driverParam: string;
|
|
704
|
+
notNull: true;
|
|
705
|
+
hasDefault: false;
|
|
706
|
+
isPrimaryKey: false;
|
|
707
|
+
isAutoincrement: false;
|
|
708
|
+
hasRuntimeDefault: false;
|
|
709
|
+
enumValues: [string, ...string[]];
|
|
710
|
+
baseColumn: never;
|
|
711
|
+
identity: undefined;
|
|
712
|
+
generated: undefined;
|
|
713
|
+
}, {}, {}>;
|
|
714
|
+
allocatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
715
|
+
name: "allocated_at";
|
|
716
|
+
tableName: "path_allocations";
|
|
717
|
+
dataType: "string";
|
|
718
|
+
columnType: "PgText";
|
|
719
|
+
data: string;
|
|
720
|
+
driverParam: string;
|
|
721
|
+
notNull: true;
|
|
722
|
+
hasDefault: true;
|
|
723
|
+
isPrimaryKey: false;
|
|
724
|
+
isAutoincrement: false;
|
|
725
|
+
hasRuntimeDefault: false;
|
|
726
|
+
enumValues: [string, ...string[]];
|
|
727
|
+
baseColumn: never;
|
|
728
|
+
identity: undefined;
|
|
729
|
+
generated: undefined;
|
|
730
|
+
}, {}, {}>;
|
|
731
|
+
};
|
|
732
|
+
dialect: "pg";
|
|
733
|
+
}>;
|
|
734
|
+
/**
|
|
735
|
+
* Webhook delivery outbox — durable retry for payment callbacks.
|
|
736
|
+
* Inserted when a payment is confirmed. Retried until the receiver ACKs.
|
|
737
|
+
*/
|
|
738
|
+
export declare const webhookDeliveries: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
739
|
+
name: "webhook_deliveries";
|
|
740
|
+
schema: undefined;
|
|
741
|
+
columns: {
|
|
742
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
743
|
+
name: "id";
|
|
744
|
+
tableName: "webhook_deliveries";
|
|
745
|
+
dataType: "number";
|
|
746
|
+
columnType: "PgInteger";
|
|
747
|
+
data: number;
|
|
748
|
+
driverParam: string | number;
|
|
749
|
+
notNull: true;
|
|
750
|
+
hasDefault: true;
|
|
751
|
+
isPrimaryKey: true;
|
|
752
|
+
isAutoincrement: false;
|
|
753
|
+
hasRuntimeDefault: false;
|
|
754
|
+
enumValues: undefined;
|
|
755
|
+
baseColumn: never;
|
|
756
|
+
identity: "always";
|
|
757
|
+
generated: undefined;
|
|
758
|
+
}, {}, {}>;
|
|
759
|
+
chargeId: import("drizzle-orm/pg-core").PgColumn<{
|
|
760
|
+
name: "charge_id";
|
|
761
|
+
tableName: "webhook_deliveries";
|
|
762
|
+
dataType: "string";
|
|
763
|
+
columnType: "PgText";
|
|
764
|
+
data: string;
|
|
765
|
+
driverParam: string;
|
|
766
|
+
notNull: true;
|
|
767
|
+
hasDefault: false;
|
|
768
|
+
isPrimaryKey: false;
|
|
769
|
+
isAutoincrement: false;
|
|
770
|
+
hasRuntimeDefault: false;
|
|
771
|
+
enumValues: [string, ...string[]];
|
|
772
|
+
baseColumn: never;
|
|
773
|
+
identity: undefined;
|
|
774
|
+
generated: undefined;
|
|
775
|
+
}, {}, {}>;
|
|
776
|
+
callbackUrl: import("drizzle-orm/pg-core").PgColumn<{
|
|
777
|
+
name: "callback_url";
|
|
778
|
+
tableName: "webhook_deliveries";
|
|
779
|
+
dataType: "string";
|
|
780
|
+
columnType: "PgText";
|
|
781
|
+
data: string;
|
|
782
|
+
driverParam: string;
|
|
783
|
+
notNull: true;
|
|
784
|
+
hasDefault: false;
|
|
785
|
+
isPrimaryKey: false;
|
|
786
|
+
isAutoincrement: false;
|
|
787
|
+
hasRuntimeDefault: false;
|
|
788
|
+
enumValues: [string, ...string[]];
|
|
789
|
+
baseColumn: never;
|
|
790
|
+
identity: undefined;
|
|
791
|
+
generated: undefined;
|
|
792
|
+
}, {}, {}>;
|
|
793
|
+
payload: import("drizzle-orm/pg-core").PgColumn<{
|
|
794
|
+
name: "payload";
|
|
795
|
+
tableName: "webhook_deliveries";
|
|
796
|
+
dataType: "string";
|
|
797
|
+
columnType: "PgText";
|
|
798
|
+
data: string;
|
|
799
|
+
driverParam: string;
|
|
800
|
+
notNull: true;
|
|
801
|
+
hasDefault: false;
|
|
802
|
+
isPrimaryKey: false;
|
|
803
|
+
isAutoincrement: false;
|
|
804
|
+
hasRuntimeDefault: false;
|
|
805
|
+
enumValues: [string, ...string[]];
|
|
806
|
+
baseColumn: never;
|
|
807
|
+
identity: undefined;
|
|
808
|
+
generated: undefined;
|
|
809
|
+
}, {}, {}>;
|
|
810
|
+
status: import("drizzle-orm/pg-core").PgColumn<{
|
|
811
|
+
name: "status";
|
|
812
|
+
tableName: "webhook_deliveries";
|
|
813
|
+
dataType: "string";
|
|
814
|
+
columnType: "PgText";
|
|
815
|
+
data: string;
|
|
816
|
+
driverParam: string;
|
|
817
|
+
notNull: true;
|
|
818
|
+
hasDefault: true;
|
|
819
|
+
isPrimaryKey: false;
|
|
820
|
+
isAutoincrement: false;
|
|
821
|
+
hasRuntimeDefault: false;
|
|
822
|
+
enumValues: [string, ...string[]];
|
|
823
|
+
baseColumn: never;
|
|
824
|
+
identity: undefined;
|
|
825
|
+
generated: undefined;
|
|
826
|
+
}, {}, {}>;
|
|
827
|
+
attempts: import("drizzle-orm/pg-core").PgColumn<{
|
|
828
|
+
name: "attempts";
|
|
829
|
+
tableName: "webhook_deliveries";
|
|
830
|
+
dataType: "number";
|
|
831
|
+
columnType: "PgInteger";
|
|
832
|
+
data: number;
|
|
833
|
+
driverParam: string | number;
|
|
834
|
+
notNull: true;
|
|
835
|
+
hasDefault: true;
|
|
836
|
+
isPrimaryKey: false;
|
|
837
|
+
isAutoincrement: false;
|
|
838
|
+
hasRuntimeDefault: false;
|
|
839
|
+
enumValues: undefined;
|
|
840
|
+
baseColumn: never;
|
|
841
|
+
identity: undefined;
|
|
842
|
+
generated: undefined;
|
|
843
|
+
}, {}, {}>;
|
|
844
|
+
nextRetryAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
845
|
+
name: "next_retry_at";
|
|
846
|
+
tableName: "webhook_deliveries";
|
|
847
|
+
dataType: "string";
|
|
848
|
+
columnType: "PgText";
|
|
849
|
+
data: string;
|
|
850
|
+
driverParam: string;
|
|
851
|
+
notNull: false;
|
|
852
|
+
hasDefault: false;
|
|
853
|
+
isPrimaryKey: false;
|
|
854
|
+
isAutoincrement: false;
|
|
855
|
+
hasRuntimeDefault: false;
|
|
856
|
+
enumValues: [string, ...string[]];
|
|
857
|
+
baseColumn: never;
|
|
858
|
+
identity: undefined;
|
|
859
|
+
generated: undefined;
|
|
860
|
+
}, {}, {}>;
|
|
861
|
+
lastError: import("drizzle-orm/pg-core").PgColumn<{
|
|
862
|
+
name: "last_error";
|
|
863
|
+
tableName: "webhook_deliveries";
|
|
864
|
+
dataType: "string";
|
|
865
|
+
columnType: "PgText";
|
|
866
|
+
data: string;
|
|
867
|
+
driverParam: string;
|
|
868
|
+
notNull: false;
|
|
869
|
+
hasDefault: false;
|
|
870
|
+
isPrimaryKey: false;
|
|
871
|
+
isAutoincrement: false;
|
|
872
|
+
hasRuntimeDefault: false;
|
|
873
|
+
enumValues: [string, ...string[]];
|
|
874
|
+
baseColumn: never;
|
|
875
|
+
identity: undefined;
|
|
876
|
+
generated: undefined;
|
|
877
|
+
}, {}, {}>;
|
|
878
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
879
|
+
name: "created_at";
|
|
880
|
+
tableName: "webhook_deliveries";
|
|
881
|
+
dataType: "string";
|
|
882
|
+
columnType: "PgText";
|
|
883
|
+
data: string;
|
|
884
|
+
driverParam: string;
|
|
885
|
+
notNull: true;
|
|
886
|
+
hasDefault: true;
|
|
887
|
+
isPrimaryKey: false;
|
|
888
|
+
isAutoincrement: false;
|
|
889
|
+
hasRuntimeDefault: false;
|
|
890
|
+
enumValues: [string, ...string[]];
|
|
891
|
+
baseColumn: never;
|
|
892
|
+
identity: undefined;
|
|
893
|
+
generated: undefined;
|
|
894
|
+
}, {}, {}>;
|
|
895
|
+
};
|
|
896
|
+
dialect: "pg";
|
|
897
|
+
}>;
|
|
898
|
+
/**
|
|
899
|
+
* Every address ever derived — immutable append-only log.
|
|
900
|
+
* Used for auditing and ensuring no address is ever reused.
|
|
901
|
+
*/
|
|
902
|
+
export declare const derivedAddresses: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
903
|
+
name: "derived_addresses";
|
|
904
|
+
schema: undefined;
|
|
905
|
+
columns: {
|
|
906
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
907
|
+
name: "id";
|
|
908
|
+
tableName: "derived_addresses";
|
|
909
|
+
dataType: "number";
|
|
910
|
+
columnType: "PgInteger";
|
|
911
|
+
data: number;
|
|
912
|
+
driverParam: string | number;
|
|
913
|
+
notNull: true;
|
|
914
|
+
hasDefault: true;
|
|
915
|
+
isPrimaryKey: true;
|
|
916
|
+
isAutoincrement: false;
|
|
917
|
+
hasRuntimeDefault: false;
|
|
918
|
+
enumValues: undefined;
|
|
919
|
+
baseColumn: never;
|
|
920
|
+
identity: "always";
|
|
921
|
+
generated: undefined;
|
|
922
|
+
}, {}, {}>;
|
|
923
|
+
chainId: import("drizzle-orm/pg-core").PgColumn<{
|
|
924
|
+
name: "chain_id";
|
|
925
|
+
tableName: "derived_addresses";
|
|
926
|
+
dataType: "string";
|
|
927
|
+
columnType: "PgText";
|
|
928
|
+
data: string;
|
|
929
|
+
driverParam: string;
|
|
930
|
+
notNull: true;
|
|
931
|
+
hasDefault: false;
|
|
932
|
+
isPrimaryKey: false;
|
|
933
|
+
isAutoincrement: false;
|
|
934
|
+
hasRuntimeDefault: false;
|
|
935
|
+
enumValues: [string, ...string[]];
|
|
936
|
+
baseColumn: never;
|
|
937
|
+
identity: undefined;
|
|
938
|
+
generated: undefined;
|
|
939
|
+
}, {}, {}>;
|
|
940
|
+
derivationIndex: import("drizzle-orm/pg-core").PgColumn<{
|
|
941
|
+
name: "derivation_index";
|
|
942
|
+
tableName: "derived_addresses";
|
|
943
|
+
dataType: "number";
|
|
944
|
+
columnType: "PgInteger";
|
|
945
|
+
data: number;
|
|
946
|
+
driverParam: string | number;
|
|
947
|
+
notNull: true;
|
|
948
|
+
hasDefault: false;
|
|
949
|
+
isPrimaryKey: false;
|
|
950
|
+
isAutoincrement: false;
|
|
951
|
+
hasRuntimeDefault: false;
|
|
952
|
+
enumValues: undefined;
|
|
953
|
+
baseColumn: never;
|
|
954
|
+
identity: undefined;
|
|
955
|
+
generated: undefined;
|
|
956
|
+
}, {}, {}>;
|
|
957
|
+
address: import("drizzle-orm/pg-core").PgColumn<{
|
|
958
|
+
name: "address";
|
|
959
|
+
tableName: "derived_addresses";
|
|
960
|
+
dataType: "string";
|
|
961
|
+
columnType: "PgText";
|
|
962
|
+
data: string;
|
|
963
|
+
driverParam: string;
|
|
964
|
+
notNull: true;
|
|
965
|
+
hasDefault: false;
|
|
966
|
+
isPrimaryKey: false;
|
|
967
|
+
isAutoincrement: false;
|
|
968
|
+
hasRuntimeDefault: false;
|
|
969
|
+
enumValues: [string, ...string[]];
|
|
970
|
+
baseColumn: never;
|
|
971
|
+
identity: undefined;
|
|
972
|
+
generated: undefined;
|
|
973
|
+
}, {}, {}>;
|
|
974
|
+
tenantId: import("drizzle-orm/pg-core").PgColumn<{
|
|
975
|
+
name: "tenant_id";
|
|
976
|
+
tableName: "derived_addresses";
|
|
977
|
+
dataType: "string";
|
|
978
|
+
columnType: "PgText";
|
|
979
|
+
data: string;
|
|
980
|
+
driverParam: string;
|
|
981
|
+
notNull: false;
|
|
982
|
+
hasDefault: false;
|
|
983
|
+
isPrimaryKey: false;
|
|
984
|
+
isAutoincrement: false;
|
|
985
|
+
hasRuntimeDefault: false;
|
|
986
|
+
enumValues: [string, ...string[]];
|
|
987
|
+
baseColumn: never;
|
|
988
|
+
identity: undefined;
|
|
989
|
+
generated: undefined;
|
|
990
|
+
}, {}, {}>;
|
|
991
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
992
|
+
name: "created_at";
|
|
993
|
+
tableName: "derived_addresses";
|
|
994
|
+
dataType: "string";
|
|
995
|
+
columnType: "PgText";
|
|
996
|
+
data: string;
|
|
997
|
+
driverParam: string;
|
|
998
|
+
notNull: true;
|
|
999
|
+
hasDefault: true;
|
|
1000
|
+
isPrimaryKey: false;
|
|
1001
|
+
isAutoincrement: false;
|
|
1002
|
+
hasRuntimeDefault: false;
|
|
1003
|
+
enumValues: [string, ...string[]];
|
|
1004
|
+
baseColumn: never;
|
|
1005
|
+
identity: undefined;
|
|
1006
|
+
generated: undefined;
|
|
1007
|
+
}, {}, {}>;
|
|
1008
|
+
};
|
|
1009
|
+
dialect: "pg";
|
|
1010
|
+
}>;
|
|
549
1011
|
/** Processed transaction IDs for watchers without block cursors (e.g. BTC). */
|
|
550
1012
|
export declare const watcherProcessed: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
551
1013
|
name: "watcher_processed";
|
package/dist/db/schema/crypto.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { sql } from "drizzle-orm";
|
|
2
2
|
import { boolean, index, integer, pgTable, primaryKey, text } from "drizzle-orm/pg-core";
|
|
3
3
|
/**
|
|
4
|
-
* Crypto payment charges — tracks the lifecycle of each
|
|
5
|
-
* reference_id is the
|
|
4
|
+
* Crypto payment charges — tracks the lifecycle of each payment.
|
|
5
|
+
* reference_id is the charge ID (e.g. "btc:bc1q...").
|
|
6
6
|
*
|
|
7
7
|
* amountUsdCents stores the requested amount in USD cents (integer).
|
|
8
8
|
* This is NOT nanodollars — Credit.fromCents() handles the conversion
|
|
@@ -22,6 +22,11 @@ export const cryptoCharges = pgTable("crypto_charges", {
|
|
|
22
22
|
token: text("token"),
|
|
23
23
|
depositAddress: text("deposit_address"),
|
|
24
24
|
derivationIndex: integer("derivation_index"),
|
|
25
|
+
callbackUrl: text("callback_url"),
|
|
26
|
+
/** Expected crypto amount in native units (e.g. "76923" sats, "50000000" USDC base units). Locked at creation. */
|
|
27
|
+
expectedAmount: text("expected_amount"),
|
|
28
|
+
/** Running total of received crypto in native units. Accumulates across partial payments. */
|
|
29
|
+
receivedAmount: text("received_amount"),
|
|
25
30
|
}, (table) => [
|
|
26
31
|
index("idx_crypto_charges_tenant").on(table.tenantId),
|
|
27
32
|
index("idx_crypto_charges_status").on(table.status),
|
|
@@ -43,12 +48,16 @@ export const watcherCursors = pgTable("watcher_cursors", {
|
|
|
43
48
|
* Payment method registry — runtime-configurable tokens/chains.
|
|
44
49
|
* Admin inserts a row to enable a new payment method. No deploy needed.
|
|
45
50
|
* Contract addresses are immutable on-chain but configurable here.
|
|
51
|
+
*
|
|
52
|
+
* nextIndex is an atomic counter for HD derivation — never reuses an index.
|
|
53
|
+
* Increment via UPDATE ... SET next_index = next_index + 1 RETURNING next_index.
|
|
46
54
|
*/
|
|
47
55
|
export const paymentMethods = pgTable("payment_methods", {
|
|
48
|
-
id: text("id").primaryKey(), // "
|
|
49
|
-
type: text("type").notNull(), // "
|
|
50
|
-
token: text("token").notNull(), // "USDC", "ETH", "BTC"
|
|
51
|
-
chain: text("chain").notNull(), // "base", "ethereum", "bitcoin"
|
|
56
|
+
id: text("id").primaryKey(), // "btc", "base-usdc", "arb-usdc", "doge"
|
|
57
|
+
type: text("type").notNull(), // "erc20", "native", "btc"
|
|
58
|
+
token: text("token").notNull(), // "USDC", "ETH", "BTC", "DOGE"
|
|
59
|
+
chain: text("chain").notNull(), // "base", "ethereum", "bitcoin", "arbitrum"
|
|
60
|
+
network: text("network").notNull().default("mainnet"), // "mainnet", "base", "arbitrum"
|
|
52
61
|
contractAddress: text("contract_address"), // null for native (ETH, BTC)
|
|
53
62
|
decimals: integer("decimals").notNull(),
|
|
54
63
|
displayName: text("display_name").notNull(),
|
|
@@ -58,8 +67,53 @@ export const paymentMethods = pgTable("payment_methods", {
|
|
|
58
67
|
oracleAddress: text("oracle_address"), // Chainlink feed address for price (null = 1:1 stablecoin)
|
|
59
68
|
xpub: text("xpub"), // HD wallet extended public key for deposit address derivation
|
|
60
69
|
confirmations: integer("confirmations").notNull().default(1),
|
|
70
|
+
nextIndex: integer("next_index").notNull().default(0), // atomic derivation counter, never reuses
|
|
61
71
|
createdAt: text("created_at").notNull().default(sql `(now())`),
|
|
62
72
|
});
|
|
73
|
+
/**
|
|
74
|
+
* BIP-44 path allocation registry — tracks which derivation paths are in use.
|
|
75
|
+
* The server knows which paths are allocated so you never collide.
|
|
76
|
+
* The seed phrase never touches the server — only xpubs.
|
|
77
|
+
*/
|
|
78
|
+
export const pathAllocations = pgTable("path_allocations", {
|
|
79
|
+
coinType: integer("coin_type").notNull(), // BIP44 coin type (0=BTC, 60=ETH, 3=DOGE, 501=SOL)
|
|
80
|
+
accountIndex: integer("account_index").notNull(), // m/44'/{coin_type}'/{index}'
|
|
81
|
+
chainId: text("chain_id").references(() => paymentMethods.id),
|
|
82
|
+
xpub: text("xpub").notNull(),
|
|
83
|
+
allocatedAt: text("allocated_at").notNull().default(sql `(now())`),
|
|
84
|
+
}, (table) => [primaryKey({ columns: [table.coinType, table.accountIndex] })]);
|
|
85
|
+
/**
|
|
86
|
+
* Webhook delivery outbox — durable retry for payment callbacks.
|
|
87
|
+
* Inserted when a payment is confirmed. Retried until the receiver ACKs.
|
|
88
|
+
*/
|
|
89
|
+
export const webhookDeliveries = pgTable("webhook_deliveries", {
|
|
90
|
+
id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
|
|
91
|
+
chargeId: text("charge_id").notNull(),
|
|
92
|
+
callbackUrl: text("callback_url").notNull(),
|
|
93
|
+
payload: text("payload").notNull(), // JSON stringified
|
|
94
|
+
status: text("status").notNull().default("pending"), // pending, delivered, failed
|
|
95
|
+
attempts: integer("attempts").notNull().default(0),
|
|
96
|
+
nextRetryAt: text("next_retry_at"),
|
|
97
|
+
lastError: text("last_error"),
|
|
98
|
+
createdAt: text("created_at").notNull().default(sql `(now())`),
|
|
99
|
+
}, (table) => [
|
|
100
|
+
index("idx_webhook_deliveries_status").on(table.status),
|
|
101
|
+
index("idx_webhook_deliveries_charge").on(table.chargeId),
|
|
102
|
+
]);
|
|
103
|
+
/**
|
|
104
|
+
* Every address ever derived — immutable append-only log.
|
|
105
|
+
* Used for auditing and ensuring no address is ever reused.
|
|
106
|
+
*/
|
|
107
|
+
export const derivedAddresses = pgTable("derived_addresses", {
|
|
108
|
+
id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
|
|
109
|
+
chainId: text("chain_id")
|
|
110
|
+
.notNull()
|
|
111
|
+
.references(() => paymentMethods.id),
|
|
112
|
+
derivationIndex: integer("derivation_index").notNull(),
|
|
113
|
+
address: text("address").notNull().unique(),
|
|
114
|
+
tenantId: text("tenant_id"),
|
|
115
|
+
createdAt: text("created_at").notNull().default(sql `(now())`),
|
|
116
|
+
}, (table) => [index("idx_derived_addresses_chain").on(table.chainId)]);
|
|
63
117
|
/** Processed transaction IDs for watchers without block cursors (e.g. BTC). */
|
|
64
118
|
export const watcherProcessed = pgTable("watcher_processed", {
|
|
65
119
|
watcherId: text("watcher_id").notNull(),
|