moltspay 0.5.3 → 0.5.4

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/index.mjs CHANGED
@@ -3541,6 +3541,980 @@ function verifyPaymentHeader(header, expectedRecipient, expectedAmount) {
3541
3541
 
3542
3542
  // src/index.ts
3543
3543
  init_cdp();
3544
+
3545
+ // src/deferred/DeferredPaymentManager.ts
3546
+ import { randomUUID as randomUUID2 } from "crypto";
3547
+
3548
+ // src/deferred/MemoryStore.ts
3549
+ import { randomUUID } from "crypto";
3550
+ var DEFAULT_TERMS = {
3551
+ netDays: 30,
3552
+ graceDays: 7,
3553
+ settlementFrequency: "on-demand"
3554
+ };
3555
+ var MemoryDeferredStore = class {
3556
+ accounts = /* @__PURE__ */ new Map();
3557
+ payments = /* @__PURE__ */ new Map();
3558
+ // Index for faster lookups
3559
+ buyerSellerIndex = /* @__PURE__ */ new Map();
3560
+ // "buyerId:sellerId" -> accountId
3561
+ makeKey(buyerId, sellerId) {
3562
+ return `${buyerId}:${sellerId}`;
3563
+ }
3564
+ // ============ Credit Accounts ============
3565
+ async createAccount(params) {
3566
+ const accountId = `ca_${randomUUID().slice(0, 8)}`;
3567
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3568
+ const account = {
3569
+ accountId,
3570
+ buyerId: params.buyerId,
3571
+ sellerId: params.sellerId,
3572
+ creditLimit: params.creditLimit,
3573
+ balance: 0,
3574
+ status: "active",
3575
+ chain: params.chain || "base",
3576
+ terms: { ...DEFAULT_TERMS, ...params.terms },
3577
+ createdAt: now,
3578
+ updatedAt: now,
3579
+ transactions: [],
3580
+ metadata: params.metadata
3581
+ };
3582
+ this.accounts.set(accountId, account);
3583
+ this.buyerSellerIndex.set(this.makeKey(params.buyerId, params.sellerId), accountId);
3584
+ return account;
3585
+ }
3586
+ async getAccount(accountId) {
3587
+ return this.accounts.get(accountId) || null;
3588
+ }
3589
+ async getAccountByBuyer(buyerId, sellerId) {
3590
+ const accountId = this.buyerSellerIndex.get(this.makeKey(buyerId, sellerId));
3591
+ if (!accountId) return null;
3592
+ return this.accounts.get(accountId) || null;
3593
+ }
3594
+ async updateAccount(accountId, updates) {
3595
+ const account = this.accounts.get(accountId);
3596
+ if (!account) {
3597
+ throw new Error(`Account not found: ${accountId}`);
3598
+ }
3599
+ const updated = {
3600
+ ...account,
3601
+ ...updates,
3602
+ accountId,
3603
+ // Prevent overwriting ID
3604
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3605
+ };
3606
+ this.accounts.set(accountId, updated);
3607
+ return updated;
3608
+ }
3609
+ async listAccounts(sellerId) {
3610
+ const result = [];
3611
+ for (const account of this.accounts.values()) {
3612
+ if (account.sellerId === sellerId) {
3613
+ result.push(account);
3614
+ }
3615
+ }
3616
+ return result;
3617
+ }
3618
+ // ============ Deferred Payments ============
3619
+ async createPayment(params) {
3620
+ const paymentId = `dp_${randomUUID().slice(0, 8)}`;
3621
+ const now = /* @__PURE__ */ new Date();
3622
+ const dueDate = new Date(now);
3623
+ dueDate.setDate(dueDate.getDate() + (params.dueInDays || 30));
3624
+ const payment = {
3625
+ paymentId,
3626
+ accountId: params.accountId,
3627
+ orderId: params.orderId,
3628
+ service: params.service,
3629
+ amount: params.amount,
3630
+ paidAmount: 0,
3631
+ status: "pending",
3632
+ dueDate: dueDate.toISOString(),
3633
+ chain: params.chain || "base",
3634
+ buyerId: params.buyerId,
3635
+ sellerAddress: params.sellerAddress,
3636
+ plan: params.plan ? { ...params.plan, completedInstallments: 0 } : void 0,
3637
+ createdAt: now.toISOString(),
3638
+ updatedAt: now.toISOString(),
3639
+ settlements: [],
3640
+ metadata: params.metadata
3641
+ };
3642
+ this.payments.set(paymentId, payment);
3643
+ if (params.accountId) {
3644
+ await this.addTransaction(params.accountId, {
3645
+ type: "charge",
3646
+ amount: params.amount,
3647
+ balanceAfter: 0,
3648
+ // Will be calculated
3649
+ reference: params.orderId,
3650
+ notes: params.service
3651
+ });
3652
+ }
3653
+ return payment;
3654
+ }
3655
+ async getPayment(paymentId) {
3656
+ return this.payments.get(paymentId) || null;
3657
+ }
3658
+ async updatePayment(paymentId, updates) {
3659
+ const payment = this.payments.get(paymentId);
3660
+ if (!payment) {
3661
+ throw new Error(`Payment not found: ${paymentId}`);
3662
+ }
3663
+ const updated = {
3664
+ ...payment,
3665
+ ...updates,
3666
+ paymentId,
3667
+ // Prevent overwriting ID
3668
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3669
+ };
3670
+ this.payments.set(paymentId, updated);
3671
+ return updated;
3672
+ }
3673
+ async listPayments(filter) {
3674
+ const now = /* @__PURE__ */ new Date();
3675
+ const result = [];
3676
+ for (const payment of this.payments.values()) {
3677
+ if (filter.accountId && payment.accountId !== filter.accountId) continue;
3678
+ if (filter.buyerId && payment.buyerId !== filter.buyerId) continue;
3679
+ if (filter.status) {
3680
+ const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
3681
+ if (!statuses.includes(payment.status)) continue;
3682
+ }
3683
+ if (filter.overdueOnly) {
3684
+ const dueDate = new Date(payment.dueDate);
3685
+ if (dueDate >= now || payment.status === "paid" || payment.status === "settled") {
3686
+ continue;
3687
+ }
3688
+ }
3689
+ result.push(payment);
3690
+ }
3691
+ return result;
3692
+ }
3693
+ // ============ Transactions ============
3694
+ async addTransaction(accountId, tx) {
3695
+ const account = this.accounts.get(accountId);
3696
+ if (!account) {
3697
+ throw new Error(`Account not found: ${accountId}`);
3698
+ }
3699
+ let newBalance = account.balance;
3700
+ if (tx.type === "charge" || tx.type === "late_fee") {
3701
+ newBalance += tx.amount;
3702
+ } else if (tx.type === "payment" || tx.type === "credit" || tx.type === "refund") {
3703
+ newBalance -= Math.abs(tx.amount);
3704
+ } else if (tx.type === "adjustment") {
3705
+ newBalance += tx.amount;
3706
+ }
3707
+ const transaction = {
3708
+ txId: `ctx_${randomUUID().slice(0, 8)}`,
3709
+ ...tx,
3710
+ balanceAfter: newBalance,
3711
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3712
+ };
3713
+ account.balance = newBalance;
3714
+ account.transactions.push(transaction);
3715
+ account.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3716
+ return transaction;
3717
+ }
3718
+ // ============ Utilities ============
3719
+ /**
3720
+ * Get all data (for debugging/export)
3721
+ */
3722
+ export() {
3723
+ return {
3724
+ accounts: Array.from(this.accounts.values()),
3725
+ payments: Array.from(this.payments.values())
3726
+ };
3727
+ }
3728
+ /**
3729
+ * Import data (for restore)
3730
+ */
3731
+ import(data) {
3732
+ this.accounts.clear();
3733
+ this.payments.clear();
3734
+ this.buyerSellerIndex.clear();
3735
+ for (const account of data.accounts) {
3736
+ this.accounts.set(account.accountId, account);
3737
+ this.buyerSellerIndex.set(this.makeKey(account.buyerId, account.sellerId), account.accountId);
3738
+ }
3739
+ for (const payment of data.payments) {
3740
+ this.payments.set(payment.paymentId, payment);
3741
+ }
3742
+ }
3743
+ /**
3744
+ * Clear all data
3745
+ */
3746
+ clear() {
3747
+ this.accounts.clear();
3748
+ this.payments.clear();
3749
+ this.buyerSellerIndex.clear();
3750
+ }
3751
+ };
3752
+
3753
+ // src/deferred/DeferredPaymentManager.ts
3754
+ var DeferredPaymentManager = class {
3755
+ config;
3756
+ constructor(config) {
3757
+ this.config = {
3758
+ sellerAddress: config.sellerAddress,
3759
+ sellerId: config.sellerId,
3760
+ chain: config.chain || "base",
3761
+ store: config.store || new MemoryDeferredStore(),
3762
+ autoVerify: config.autoVerify ?? true
3763
+ };
3764
+ }
3765
+ // ============ Credit Account Management ============
3766
+ /**
3767
+ * Create a new credit account for a buyer
3768
+ */
3769
+ async createCreditAccount(params) {
3770
+ const existing = await this.config.store.getAccountByBuyer(
3771
+ params.buyerId,
3772
+ this.config.sellerId
3773
+ );
3774
+ if (existing) {
3775
+ throw new Error(`Credit account already exists for buyer: ${params.buyerId}`);
3776
+ }
3777
+ return this.config.store.createAccount({
3778
+ buyerId: params.buyerId,
3779
+ sellerId: this.config.sellerId,
3780
+ creditLimit: params.creditLimit,
3781
+ chain: this.config.chain,
3782
+ terms: params.netDays ? { netDays: params.netDays, graceDays: 7, settlementFrequency: "on-demand" } : void 0,
3783
+ metadata: params.metadata
3784
+ });
3785
+ }
3786
+ /**
3787
+ * Get or create a credit account for a buyer
3788
+ */
3789
+ async getOrCreateAccount(params) {
3790
+ const existing = await this.config.store.getAccountByBuyer(
3791
+ params.buyerId,
3792
+ this.config.sellerId
3793
+ );
3794
+ if (existing) return existing;
3795
+ return this.createCreditAccount({
3796
+ buyerId: params.buyerId,
3797
+ creditLimit: params.creditLimit || 100
3798
+ // Default $100 credit limit
3799
+ });
3800
+ }
3801
+ /**
3802
+ * Get credit account by ID
3803
+ */
3804
+ async getAccount(accountId) {
3805
+ return this.config.store.getAccount(accountId);
3806
+ }
3807
+ /**
3808
+ * Get credit account by buyer ID
3809
+ */
3810
+ async getAccountByBuyer(buyerId) {
3811
+ return this.config.store.getAccountByBuyer(buyerId, this.config.sellerId);
3812
+ }
3813
+ /**
3814
+ * Update credit limit
3815
+ */
3816
+ async updateCreditLimit(accountId, newLimit) {
3817
+ return this.config.store.updateAccount(accountId, { creditLimit: newLimit });
3818
+ }
3819
+ /**
3820
+ * Suspend an account
3821
+ */
3822
+ async suspendAccount(accountId, reason) {
3823
+ const account = await this.config.store.updateAccount(accountId, { status: "suspended" });
3824
+ await this.config.store.addTransaction(accountId, {
3825
+ type: "adjustment",
3826
+ amount: 0,
3827
+ balanceAfter: account.balance,
3828
+ reference: "account_suspended",
3829
+ notes: reason || "Account suspended"
3830
+ });
3831
+ return account;
3832
+ }
3833
+ /**
3834
+ * Reactivate a suspended account
3835
+ */
3836
+ async reactivateAccount(accountId) {
3837
+ return this.config.store.updateAccount(accountId, { status: "active" });
3838
+ }
3839
+ /**
3840
+ * Get account summary with pending/overdue payments
3841
+ */
3842
+ async getAccountSummary(accountId) {
3843
+ const account = await this.config.store.getAccount(accountId);
3844
+ if (!account) return null;
3845
+ const allPayments = await this.config.store.listPayments({ accountId });
3846
+ const now = /* @__PURE__ */ new Date();
3847
+ const pendingPayments = allPayments.filter(
3848
+ (p) => p.status === "pending" || p.status === "partial"
3849
+ );
3850
+ const overduePayments = allPayments.filter((p) => {
3851
+ if (p.status === "paid" || p.status === "settled" || p.status === "cancelled") {
3852
+ return false;
3853
+ }
3854
+ return new Date(p.dueDate) < now;
3855
+ });
3856
+ return {
3857
+ account,
3858
+ pendingPayments,
3859
+ overduePayments,
3860
+ availableCredit: Math.max(0, account.creditLimit - account.balance),
3861
+ totalOwed: account.balance
3862
+ };
3863
+ }
3864
+ /**
3865
+ * List all credit accounts for this seller
3866
+ */
3867
+ async listAccounts() {
3868
+ return this.config.store.listAccounts(this.config.sellerId);
3869
+ }
3870
+ // ============ Deferred Payment Operations ============
3871
+ /**
3872
+ * Charge a service to a credit account (deferred payment)
3873
+ */
3874
+ async charge(params) {
3875
+ let account = await this.getAccountByBuyer(params.buyerId);
3876
+ if (!account) {
3877
+ try {
3878
+ account = await this.createCreditAccount({
3879
+ buyerId: params.buyerId,
3880
+ creditLimit: 100
3881
+ // Default $100 limit
3882
+ });
3883
+ } catch (error) {
3884
+ return {
3885
+ success: false,
3886
+ error: `Failed to create credit account: ${error}`
3887
+ };
3888
+ }
3889
+ }
3890
+ if (account.status !== "active") {
3891
+ return {
3892
+ success: false,
3893
+ error: `Credit account is ${account.status}`
3894
+ };
3895
+ }
3896
+ const newBalance = account.balance + params.amount;
3897
+ if (newBalance > account.creditLimit) {
3898
+ return {
3899
+ success: false,
3900
+ error: `Credit limit exceeded. Available: $${(account.creditLimit - account.balance).toFixed(2)}, Required: $${params.amount.toFixed(2)}`,
3901
+ creditExceeded: true
3902
+ };
3903
+ }
3904
+ const payment = await this.config.store.createPayment({
3905
+ accountId: account.accountId,
3906
+ orderId: params.orderId,
3907
+ service: params.service,
3908
+ amount: params.amount,
3909
+ buyerId: params.buyerId,
3910
+ sellerAddress: this.config.sellerAddress,
3911
+ chain: this.config.chain,
3912
+ dueInDays: params.dueInDays || account.terms.netDays,
3913
+ metadata: params.metadata
3914
+ });
3915
+ const updatedAccount = await this.config.store.getAccount(account.accountId);
3916
+ const lastTx = updatedAccount?.transactions[updatedAccount.transactions.length - 1];
3917
+ return {
3918
+ success: true,
3919
+ payment,
3920
+ transaction: lastTx
3921
+ };
3922
+ }
3923
+ /**
3924
+ * Create a deferred payment without a credit account (standalone)
3925
+ */
3926
+ async createDeferredPayment(params) {
3927
+ return this.config.store.createPayment({
3928
+ ...params,
3929
+ sellerAddress: params.sellerAddress || this.config.sellerAddress,
3930
+ chain: params.chain || this.config.chain
3931
+ });
3932
+ }
3933
+ /**
3934
+ * Get deferred payment by ID
3935
+ */
3936
+ async getPayment(paymentId) {
3937
+ return this.config.store.getPayment(paymentId);
3938
+ }
3939
+ /**
3940
+ * List deferred payments with filters
3941
+ */
3942
+ async listPayments(filter) {
3943
+ return this.config.store.listPayments(filter || {});
3944
+ }
3945
+ /**
3946
+ * Get overdue payments
3947
+ */
3948
+ async getOverduePayments() {
3949
+ return this.config.store.listPayments({ overdueOnly: true });
3950
+ }
3951
+ // ============ Settlement ============
3952
+ /**
3953
+ * Record a settlement (payment received)
3954
+ */
3955
+ async recordSettlement(params) {
3956
+ const payment = await this.config.store.getPayment(params.paymentId);
3957
+ if (!payment) {
3958
+ return { success: false, error: `Payment not found: ${params.paymentId}` };
3959
+ }
3960
+ let verified = false;
3961
+ if (this.config.autoVerify) {
3962
+ try {
3963
+ const verification = await verifyPayment({
3964
+ txHash: params.txHash,
3965
+ chain: params.chain || payment.chain,
3966
+ expectedTo: payment.sellerAddress,
3967
+ expectedAmount: params.amount
3968
+ });
3969
+ verified = verification.verified;
3970
+ if (!verified) {
3971
+ return {
3972
+ success: false,
3973
+ error: `Payment verification failed: ${verification.error || "Unknown error"}`
3974
+ };
3975
+ }
3976
+ } catch (error) {
3977
+ return {
3978
+ success: false,
3979
+ error: `Verification error: ${error}`
3980
+ };
3981
+ }
3982
+ } else {
3983
+ verified = true;
3984
+ }
3985
+ const settlement = {
3986
+ settlementId: `stl_${randomUUID2().slice(0, 8)}`,
3987
+ amount: params.amount,
3988
+ txHash: params.txHash,
3989
+ chain: params.chain || payment.chain,
3990
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3991
+ verified
3992
+ };
3993
+ const newPaidAmount = payment.paidAmount + params.amount;
3994
+ const newStatus = newPaidAmount >= payment.amount ? "settled" : newPaidAmount > 0 ? "partial" : "pending";
3995
+ const updatedPayment = await this.config.store.updatePayment(params.paymentId, {
3996
+ paidAmount: newPaidAmount,
3997
+ status: newStatus,
3998
+ settlements: [...payment.settlements, settlement]
3999
+ });
4000
+ let transaction;
4001
+ if (payment.accountId) {
4002
+ transaction = await this.config.store.addTransaction(payment.accountId, {
4003
+ type: "payment",
4004
+ amount: params.amount,
4005
+ balanceAfter: 0,
4006
+ // Will be calculated by store
4007
+ reference: payment.orderId,
4008
+ onChainTxHash: params.txHash,
4009
+ notes: `Settlement for ${payment.service}`
4010
+ });
4011
+ }
4012
+ return {
4013
+ success: true,
4014
+ settlement,
4015
+ payment: updatedPayment,
4016
+ transaction
4017
+ };
4018
+ }
4019
+ /**
4020
+ * Settle all pending charges for an account
4021
+ */
4022
+ async settleAccount(accountId, txHash) {
4023
+ const account = await this.config.store.getAccount(accountId);
4024
+ if (!account) {
4025
+ return { success: false, error: `Account not found: ${accountId}` };
4026
+ }
4027
+ if (account.balance <= 0) {
4028
+ return { success: false, error: "No balance to settle" };
4029
+ }
4030
+ if (this.config.autoVerify) {
4031
+ const verification = await verifyPayment({
4032
+ txHash,
4033
+ chain: account.chain,
4034
+ expectedTo: this.config.sellerAddress,
4035
+ expectedAmount: account.balance
4036
+ });
4037
+ if (!verification.verified) {
4038
+ return {
4039
+ success: false,
4040
+ error: `Payment verification failed: ${verification.error || "Unknown error"}`
4041
+ };
4042
+ }
4043
+ }
4044
+ const transaction = await this.config.store.addTransaction(accountId, {
4045
+ type: "payment",
4046
+ amount: account.balance,
4047
+ balanceAfter: 0,
4048
+ reference: `account_settlement_${accountId}`,
4049
+ onChainTxHash: txHash,
4050
+ notes: "Full account settlement"
4051
+ });
4052
+ const pendingPayments = await this.config.store.listPayments({
4053
+ accountId,
4054
+ status: ["pending", "partial"]
4055
+ });
4056
+ const settlement = {
4057
+ settlementId: `stl_${randomUUID2().slice(0, 8)}`,
4058
+ amount: account.balance,
4059
+ txHash,
4060
+ chain: account.chain,
4061
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4062
+ verified: true
4063
+ };
4064
+ for (const payment of pendingPayments) {
4065
+ await this.config.store.updatePayment(payment.paymentId, {
4066
+ paidAmount: payment.amount,
4067
+ status: "settled",
4068
+ settlements: [...payment.settlements, settlement]
4069
+ });
4070
+ }
4071
+ return {
4072
+ success: true,
4073
+ settlement,
4074
+ transaction
4075
+ };
4076
+ }
4077
+ // ============ Credit Operations ============
4078
+ /**
4079
+ * Issue a credit (reduce balance owed)
4080
+ */
4081
+ async issueCredit(params) {
4082
+ return this.config.store.addTransaction(params.accountId, {
4083
+ type: "credit",
4084
+ amount: params.amount,
4085
+ balanceAfter: 0,
4086
+ reference: "credit_issued",
4087
+ notes: params.reason
4088
+ });
4089
+ }
4090
+ /**
4091
+ * Issue a refund
4092
+ */
4093
+ async issueRefund(params) {
4094
+ const payment = await this.config.store.getPayment(params.paymentId);
4095
+ if (!payment) {
4096
+ return { success: false, error: `Payment not found: ${params.paymentId}` };
4097
+ }
4098
+ const newPaidAmount = Math.max(0, payment.paidAmount - params.amount);
4099
+ await this.config.store.updatePayment(params.paymentId, {
4100
+ paidAmount: newPaidAmount,
4101
+ status: newPaidAmount >= payment.amount ? "settled" : newPaidAmount > 0 ? "partial" : "pending"
4102
+ });
4103
+ let transaction;
4104
+ if (payment.accountId) {
4105
+ transaction = await this.config.store.addTransaction(payment.accountId, {
4106
+ type: "refund",
4107
+ amount: params.amount,
4108
+ balanceAfter: 0,
4109
+ reference: payment.orderId,
4110
+ notes: params.reason
4111
+ });
4112
+ }
4113
+ return { success: true, transaction };
4114
+ }
4115
+ // ============ Maintenance ============
4116
+ /**
4117
+ * Mark overdue payments
4118
+ */
4119
+ async markOverduePayments() {
4120
+ const now = /* @__PURE__ */ new Date();
4121
+ const pending = await this.config.store.listPayments({
4122
+ status: ["pending", "partial"]
4123
+ });
4124
+ const overdue = [];
4125
+ for (const payment of pending) {
4126
+ if (new Date(payment.dueDate) < now) {
4127
+ const updated = await this.config.store.updatePayment(payment.paymentId, {
4128
+ status: "overdue"
4129
+ });
4130
+ overdue.push(updated);
4131
+ if (payment.accountId) {
4132
+ const account = await this.config.store.getAccount(payment.accountId);
4133
+ if (account && account.status === "active") {
4134
+ const graceEnd = new Date(payment.dueDate);
4135
+ graceEnd.setDate(graceEnd.getDate() + account.terms.graceDays);
4136
+ if (now > graceEnd) {
4137
+ await this.suspendAccount(payment.accountId, "Overdue payment past grace period");
4138
+ }
4139
+ }
4140
+ }
4141
+ }
4142
+ }
4143
+ return overdue;
4144
+ }
4145
+ /**
4146
+ * Apply late fees to overdue accounts
4147
+ */
4148
+ async applyLateFees() {
4149
+ const accounts = await this.listAccounts();
4150
+ const fees = [];
4151
+ for (const account of accounts) {
4152
+ if (account.terms.lateFeePercent && account.balance > 0) {
4153
+ const overduePayments = await this.config.store.listPayments({
4154
+ accountId: account.accountId,
4155
+ status: "overdue"
4156
+ });
4157
+ if (overduePayments.length > 0) {
4158
+ const feeAmount = account.balance * (account.terms.lateFeePercent / 100);
4159
+ const tx = await this.config.store.addTransaction(account.accountId, {
4160
+ type: "late_fee",
4161
+ amount: feeAmount,
4162
+ balanceAfter: 0,
4163
+ reference: "late_fee",
4164
+ notes: `${account.terms.lateFeePercent}% late fee on $${account.balance.toFixed(2)}`
4165
+ });
4166
+ fees.push(tx);
4167
+ }
4168
+ }
4169
+ }
4170
+ return fees;
4171
+ }
4172
+ };
4173
+
4174
+ // src/deferred/JsonStore.ts
4175
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
4176
+ import { dirname as dirname2 } from "path";
4177
+ var JsonDeferredStore = class {
4178
+ memory;
4179
+ config;
4180
+ dirty = false;
4181
+ constructor(config) {
4182
+ this.config = {
4183
+ filePath: config.filePath,
4184
+ autoSave: config.autoSave ?? true,
4185
+ prettyPrint: config.prettyPrint ?? true
4186
+ };
4187
+ this.memory = new MemoryDeferredStore();
4188
+ this.load();
4189
+ }
4190
+ // ============ Persistence ============
4191
+ /**
4192
+ * Load data from file
4193
+ */
4194
+ load() {
4195
+ if (!existsSync5(this.config.filePath)) {
4196
+ return;
4197
+ }
4198
+ try {
4199
+ const data = JSON.parse(readFileSync5(this.config.filePath, "utf-8"));
4200
+ this.memory.import(data);
4201
+ } catch (error) {
4202
+ console.error(`Failed to load deferred store from ${this.config.filePath}:`, error);
4203
+ }
4204
+ }
4205
+ /**
4206
+ * Save data to file
4207
+ */
4208
+ save() {
4209
+ try {
4210
+ const dir = dirname2(this.config.filePath);
4211
+ if (!existsSync5(dir)) {
4212
+ mkdirSync5(dir, { recursive: true });
4213
+ }
4214
+ const data = this.memory.export();
4215
+ const json = this.config.prettyPrint ? JSON.stringify(data, null, 2) : JSON.stringify(data);
4216
+ writeFileSync4(this.config.filePath, json, "utf-8");
4217
+ this.dirty = false;
4218
+ } catch (error) {
4219
+ console.error(`Failed to save deferred store to ${this.config.filePath}:`, error);
4220
+ throw error;
4221
+ }
4222
+ }
4223
+ /**
4224
+ * Mark as dirty and optionally auto-save
4225
+ */
4226
+ markDirty() {
4227
+ this.dirty = true;
4228
+ if (this.config.autoSave) {
4229
+ this.save();
4230
+ }
4231
+ }
4232
+ /**
4233
+ * Check if there are unsaved changes
4234
+ */
4235
+ isDirty() {
4236
+ return this.dirty;
4237
+ }
4238
+ // ============ Credit Accounts (delegate to memory store) ============
4239
+ async createAccount(params) {
4240
+ const result = await this.memory.createAccount(params);
4241
+ this.markDirty();
4242
+ return result;
4243
+ }
4244
+ async getAccount(accountId) {
4245
+ return this.memory.getAccount(accountId);
4246
+ }
4247
+ async getAccountByBuyer(buyerId, sellerId) {
4248
+ return this.memory.getAccountByBuyer(buyerId, sellerId);
4249
+ }
4250
+ async updateAccount(accountId, updates) {
4251
+ const result = await this.memory.updateAccount(accountId, updates);
4252
+ this.markDirty();
4253
+ return result;
4254
+ }
4255
+ async listAccounts(sellerId) {
4256
+ return this.memory.listAccounts(sellerId);
4257
+ }
4258
+ // ============ Deferred Payments ============
4259
+ async createPayment(params) {
4260
+ const result = await this.memory.createPayment(params);
4261
+ this.markDirty();
4262
+ return result;
4263
+ }
4264
+ async getPayment(paymentId) {
4265
+ return this.memory.getPayment(paymentId);
4266
+ }
4267
+ async updatePayment(paymentId, updates) {
4268
+ const result = await this.memory.updatePayment(paymentId, updates);
4269
+ this.markDirty();
4270
+ return result;
4271
+ }
4272
+ async listPayments(filter) {
4273
+ return this.memory.listPayments(filter);
4274
+ }
4275
+ // ============ Transactions ============
4276
+ async addTransaction(accountId, tx) {
4277
+ const result = await this.memory.addTransaction(accountId, tx);
4278
+ this.markDirty();
4279
+ return result;
4280
+ }
4281
+ // ============ Utilities ============
4282
+ /**
4283
+ * Export all data
4284
+ */
4285
+ export() {
4286
+ return this.memory.export();
4287
+ }
4288
+ /**
4289
+ * Clear all data (including file)
4290
+ */
4291
+ clear() {
4292
+ this.memory.clear();
4293
+ this.markDirty();
4294
+ }
4295
+ };
4296
+
4297
+ // src/deferred/templates.ts
4298
+ var DeferredStatusMarkers = {
4299
+ creditAccountCreated: (accountId, limit) => `[status:credit_account_created id=${accountId} limit=${limit} USDC]`,
4300
+ chargeAdded: (paymentId, amount) => `[status:charge_added payment=${paymentId} amount=${amount} USDC]`,
4301
+ settlementReceived: (paymentId, txHash, amount) => `[status:settlement_received payment=${paymentId} tx=${txHash} amount=${amount} USDC]`,
4302
+ accountSettled: (accountId, txHash, amount) => `[status:account_settled id=${accountId} tx=${txHash} amount=${amount} USDC]`,
4303
+ creditIssued: (accountId, amount) => `[status:credit_issued id=${accountId} amount=${amount} USDC]`,
4304
+ accountSuspended: (accountId, reason) => `[status:account_suspended id=${accountId} reason="${reason}"]`,
4305
+ paymentOverdue: (paymentId, amount) => `[status:payment_overdue payment=${paymentId} amount=${amount} USDC]`
4306
+ };
4307
+ var DeferredSellerTemplates = {
4308
+ /**
4309
+ * Offer deferred payment option
4310
+ */
4311
+ offerDeferredPayment: (params) => {
4312
+ const days = params.netDays || 30;
4313
+ return `I can offer you two payment options for ${params.service}:
4314
+
4315
+ **Option A: Pay Now**
4316
+ - Price: ${params.price} USDC
4317
+ - Pay via on-chain transfer, service delivered immediately
4318
+
4319
+ **Option B: Pay Later (Net-${days})**
4320
+ - Price: ${params.price} USDC
4321
+ - Service delivered now, pay within ${days} days
4322
+ - Requires a credit account (I can set one up for you)
4323
+
4324
+ Which option do you prefer?`;
4325
+ },
4326
+ /**
4327
+ * Explain credit account setup
4328
+ */
4329
+ explainCreditAccount: () => `
4330
+ To use deferred payment, I'll set up a credit account for you. Here's how it works:
4331
+
4332
+ 1. **Credit Limit** - I'll extend you a credit line (e.g., $100 USDC)
4333
+ 2. **Use Services** - Each service gets charged to your account
4334
+ 3. **Pay Later** - Settle your balance whenever you want, or by the due date
4335
+ 4. **On-chain Settlement** - When you pay, send USDC to my address and share the tx hash
4336
+
4337
+ Would you like me to set up a credit account for you?`,
4338
+ /**
4339
+ * Confirm credit account creation
4340
+ */
4341
+ creditAccountCreated: (account) => `Great! I've set up a credit account for you.
4342
+
4343
+ **Account Details:**
4344
+ - Account ID: ${account.accountId}
4345
+ - Credit Limit: $${account.creditLimit.toFixed(2)} USDC
4346
+ - Payment Terms: Net-${account.terms.netDays}
4347
+ - Current Balance: $${account.balance.toFixed(2)}
4348
+
4349
+ You can now use services on credit. I'll track charges and you can settle anytime.
4350
+ ${DeferredStatusMarkers.creditAccountCreated(account.accountId, account.creditLimit)}`,
4351
+ /**
4352
+ * Charge confirmation
4353
+ */
4354
+ chargeConfirmation: (payment, availableCredit) => `Service charged to your account.
4355
+
4356
+ **Charge Details:**
4357
+ - Service: ${payment.service}
4358
+ - Amount: $${payment.amount.toFixed(2)} USDC
4359
+ - Order ID: ${payment.orderId}
4360
+ - Due Date: ${new Date(payment.dueDate).toLocaleDateString()}
4361
+
4362
+ **Account Status:**
4363
+ - Available Credit: $${availableCredit.toFixed(2)} USDC
4364
+
4365
+ I'll proceed with your service now.
4366
+ ${DeferredStatusMarkers.chargeAdded(payment.paymentId, payment.amount)}`,
4367
+ /**
4368
+ * Credit limit exceeded
4369
+ */
4370
+ creditLimitExceeded: (params) => `Sorry, this charge would exceed your credit limit.
4371
+
4372
+ **Current Status:**
4373
+ - Credit Limit: $${params.limit.toFixed(2)} USDC
4374
+ - Current Balance: $${params.balance.toFixed(2)} USDC
4375
+ - Available Credit: $${params.available.toFixed(2)} USDC
4376
+ - Requested: $${params.requested.toFixed(2)} USDC
4377
+
4378
+ You can either:
4379
+ 1. **Settle some balance** - Pay down your balance to free up credit
4380
+ 2. **Pay for this service directly** - Skip credit, pay on-chain now
4381
+ 3. **Request credit increase** - Ask for a higher limit (subject to approval)
4382
+
4383
+ What would you like to do?`,
4384
+ /**
4385
+ * Account summary/statement
4386
+ */
4387
+ accountStatement: (summary) => {
4388
+ const { account, pendingPayments, overduePayments } = summary;
4389
+ let statement = `**Account Statement**
4390
+ - Account ID: ${account.accountId}
4391
+ - Status: ${account.status.toUpperCase()}
4392
+ - Credit Limit: $${account.creditLimit.toFixed(2)} USDC
4393
+ - Current Balance: $${account.balance.toFixed(2)} USDC
4394
+ - Available Credit: $${summary.availableCredit.toFixed(2)} USDC
4395
+
4396
+ `;
4397
+ if (pendingPayments.length > 0) {
4398
+ statement += `**Pending Charges (${pendingPayments.length}):**
4399
+ `;
4400
+ for (const p of pendingPayments) {
4401
+ statement += `- ${p.service}: $${p.amount.toFixed(2)} (due ${new Date(p.dueDate).toLocaleDateString()})
4402
+ `;
4403
+ }
4404
+ statement += "\n";
4405
+ }
4406
+ if (overduePayments.length > 0) {
4407
+ statement += `**\u26A0\uFE0F OVERDUE (${overduePayments.length}):**
4408
+ `;
4409
+ for (const p of overduePayments) {
4410
+ statement += `- ${p.service}: $${p.amount.toFixed(2)} (was due ${new Date(p.dueDate).toLocaleDateString()})
4411
+ `;
4412
+ }
4413
+ statement += "\n";
4414
+ }
4415
+ if (account.balance > 0) {
4416
+ statement += `**To Settle:**
4417
+ Send $${account.balance.toFixed(2)} USDC to my address and share the transaction hash.`;
4418
+ }
4419
+ return statement;
4420
+ },
4421
+ /**
4422
+ * Settlement confirmation
4423
+ */
4424
+ settlementConfirmation: (params) => `Payment received and verified on-chain. Thank you!
4425
+
4426
+ **Settlement Details:**
4427
+ - Amount: $${params.amount.toFixed(2)} USDC
4428
+ - Transaction: ${params.txHash}
4429
+ - New Balance: $${params.newBalance.toFixed(2)} USDC
4430
+
4431
+ ${params.paymentId ? DeferredStatusMarkers.settlementReceived(params.paymentId, params.txHash, params.amount) : ""}`,
4432
+ /**
4433
+ * Overdue notice
4434
+ */
4435
+ overdueNotice: (payments) => {
4436
+ const total = payments.reduce((sum, p) => sum + (p.amount - p.paidAmount), 0);
4437
+ let notice = `\u26A0\uFE0F **Payment Overdue Notice**
4438
+
4439
+ You have ${payments.length} overdue payment(s) totaling $${total.toFixed(2)} USDC:
4440
+
4441
+ `;
4442
+ for (const p of payments) {
4443
+ const overdueDays = Math.floor((Date.now() - new Date(p.dueDate).getTime()) / (1e3 * 60 * 60 * 24));
4444
+ notice += `- ${p.service}: $${(p.amount - p.paidAmount).toFixed(2)} (${overdueDays} days overdue)
4445
+ `;
4446
+ }
4447
+ notice += `
4448
+ Please settle your balance to avoid account suspension. Send USDC to my address and share the transaction hash.`;
4449
+ return notice;
4450
+ },
4451
+ /**
4452
+ * Credit issued confirmation
4453
+ */
4454
+ creditIssued: (params) => `I've issued a credit to your account.
4455
+
4456
+ **Credit Details:**
4457
+ - Amount: $${params.amount.toFixed(2)} USDC
4458
+ - Reason: ${params.reason}
4459
+ - New Balance: $${params.newBalance.toFixed(2)} USDC
4460
+
4461
+ ${DeferredStatusMarkers.creditIssued(params.accountId, params.amount)}`
4462
+ };
4463
+ var DeferredBuyerTemplates = {
4464
+ /**
4465
+ * Request deferred payment
4466
+ */
4467
+ requestDeferredPayment: (service) => `I'd like to use ${service}, but prefer to pay later. Do you offer deferred payment or credit terms?`,
4468
+ /**
4469
+ * Accept credit account offer
4470
+ */
4471
+ acceptCreditAccount: () => `Yes, please set up a credit account for me. I'll settle the balance by the due date.`,
4472
+ /**
4473
+ * Request credit limit increase
4474
+ */
4475
+ requestCreditIncrease: (currentLimit, requestedLimit) => `My current credit limit is $${currentLimit.toFixed(2)}. Could you increase it to $${requestedLimit.toFixed(2)}? I have a larger purchase coming up.`,
4476
+ /**
4477
+ * Request account statement
4478
+ */
4479
+ requestStatement: () => `Can you show me my current account balance and any pending charges?`,
4480
+ /**
4481
+ * Announce settlement payment
4482
+ */
4483
+ announceSettlement: (params) => `I've sent a payment to settle my balance.
4484
+
4485
+ **Payment Details:**
4486
+ - Amount: $${params.amount.toFixed(2)} USDC
4487
+ - Transaction Hash: ${params.txHash}
4488
+
4489
+ Please verify and update my account.
4490
+ ${params.accountId ? `[status:settlement_sent account=${params.accountId} tx=${params.txHash} amount=${params.amount} USDC]` : ""}`,
4491
+ /**
4492
+ * Dispute a charge
4493
+ */
4494
+ disputeCharge: (paymentId, reason) => `I'd like to dispute charge ${paymentId}. Reason: ${reason}
4495
+
4496
+ Please review and let me know how to resolve this.
4497
+ [status:charge_disputed payment=${paymentId}]`
4498
+ };
4499
+ function parseDeferredStatusMarker(text) {
4500
+ const match = text.match(/\[status:(\w+)\s+(.+?)\]/);
4501
+ if (!match) return null;
4502
+ const type = match[1];
4503
+ const dataStr = match[2];
4504
+ const data = {};
4505
+ const pairs = dataStr.match(/(\w+)=("[^"]+"|[\w.]+)/g);
4506
+ if (pairs) {
4507
+ for (const pair of pairs) {
4508
+ const [key, ...valueParts] = pair.split("=");
4509
+ let value = valueParts.join("=");
4510
+ if (value.startsWith('"') && value.endsWith('"')) {
4511
+ value = value.slice(1, -1);
4512
+ }
4513
+ data[key] = value;
4514
+ }
4515
+ }
4516
+ return { type, data };
4517
+ }
3544
4518
  export {
3545
4519
  AgentWallet,
3546
4520
  AllowanceWallet,
@@ -3548,7 +4522,13 @@ export {
3548
4522
  BuyerTemplates,
3549
4523
  CDPWallet,
3550
4524
  CHAINS,
4525
+ DeferredBuyerTemplates,
4526
+ DeferredPaymentManager,
4527
+ DeferredSellerTemplates,
4528
+ DeferredStatusMarkers,
3551
4529
  ERC20_ABI,
4530
+ JsonDeferredStore,
4531
+ MemoryDeferredStore,
3552
4532
  MemoryOrderStore,
3553
4533
  OrderManager,
3554
4534
  PAYMENT_HEADER,
@@ -3594,6 +4574,7 @@ export {
3594
4574
  loadCDPWallet,
3595
4575
  loadWallet,
3596
4576
  networkToChain,
4577
+ parseDeferredStatusMarker,
3597
4578
  parsePaymentRequired,
3598
4579
  parseStatusMarker,
3599
4580
  signEIP3009,