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