@settlr/sdk 0.4.4 → 0.6.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/README.md +77 -0
- package/dist/index.d.mts +269 -1
- package/dist/index.d.ts +269 -1
- package/dist/index.js +218 -0
- package/dist/index.mjs +207 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -18,8 +18,85 @@
|
|
|
18
18
|
- ✅ **Pay from any chain** - Accept USDC from Ethereum, Base, Arbitrum, Polygon, Optimism (Mayan)
|
|
19
19
|
- ✅ **Instant settlement** - USDC direct to your Solana wallet
|
|
20
20
|
- ✅ **One component** - Drop-in React `<BuyButton>`
|
|
21
|
+
- ✅ **Privacy-preserving** - FHE-encrypted receipts via Inco Lightning
|
|
22
|
+
- ✅ **One-click payments** - Returning customers pay instantly ⭐ NEW
|
|
21
23
|
- ✅ **2% flat fee** - No hidden costs
|
|
22
24
|
|
|
25
|
+
## ⚡ One-Click Payments (NEW)
|
|
26
|
+
|
|
27
|
+
Enable frictionless repeat purchases for returning customers:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { createOneClickClient } from "@settlr/sdk";
|
|
31
|
+
|
|
32
|
+
const oneClick = createOneClickClient("https://settlr.dev");
|
|
33
|
+
|
|
34
|
+
// Step 1: Customer approves a spending limit (once)
|
|
35
|
+
await oneClick.approve({
|
|
36
|
+
customerWallet: "Ac52MM...",
|
|
37
|
+
merchantWallet: "DjLFeM...",
|
|
38
|
+
spendingLimit: 100, // $100 max
|
|
39
|
+
expiresInDays: 30,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Step 2: Merchant charges customer later (no popups!)
|
|
43
|
+
const result = await oneClick.charge({
|
|
44
|
+
customerWallet: "Ac52MM...",
|
|
45
|
+
merchantWallet: "DjLFeM...",
|
|
46
|
+
amount: 25,
|
|
47
|
+
memo: "Premium content",
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
console.log(result.txSignature); // Payment completed instantly!
|
|
51
|
+
console.log(result.remainingLimit); // $75 left
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Use cases:**
|
|
55
|
+
|
|
56
|
+
- Gaming: Buy in-game items without interrupting gameplay
|
|
57
|
+
- Subscriptions: Charge monthly without re-authentication
|
|
58
|
+
- Microtransactions: Seamless small purchases
|
|
59
|
+
|
|
60
|
+
## 🔒 Privacy Features (Inco Lightning)
|
|
61
|
+
|
|
62
|
+
Settlr now supports **private payment receipts** using Inco Lightning's Fully Homomorphic Encryption:
|
|
63
|
+
|
|
64
|
+
- **Private on-chain** - Payment amounts are encrypted, competitors can't see your revenue
|
|
65
|
+
- **Compliant off-chain** - Merchants can still decrypt for accounting/tax (CSV export works!)
|
|
66
|
+
- **Selective disclosure** - Only customer + merchant can view the actual amount
|
|
67
|
+
- **Trustless decryption** - Inco covalidator network ensures no single point of trust
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import {
|
|
71
|
+
findPrivateReceiptPda,
|
|
72
|
+
buildPrivateReceiptAccounts,
|
|
73
|
+
encryptAmount,
|
|
74
|
+
PrivacyFeatures,
|
|
75
|
+
} from "@settlr/sdk";
|
|
76
|
+
|
|
77
|
+
// Check privacy capabilities
|
|
78
|
+
console.log(PrivacyFeatures);
|
|
79
|
+
// {
|
|
80
|
+
// ENCRYPTED_AMOUNTS: true,
|
|
81
|
+
// ACCESS_CONTROL: true,
|
|
82
|
+
// ACCOUNTING_COMPATIBLE: true,
|
|
83
|
+
// TRUSTLESS_DECRYPTION: true
|
|
84
|
+
// }
|
|
85
|
+
|
|
86
|
+
// Derive PDA for a private receipt
|
|
87
|
+
const [receiptPda] = findPrivateReceiptPda("payment_123");
|
|
88
|
+
|
|
89
|
+
// Build accounts for issuing private receipt
|
|
90
|
+
const accounts = await buildPrivateReceiptAccounts({
|
|
91
|
+
paymentId: "payment_123",
|
|
92
|
+
amount: 99.99,
|
|
93
|
+
customer: customerWallet,
|
|
94
|
+
merchant: merchantWallet,
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
> **Private on-chain. Compliant off-chain.** Your competitors can't see your revenue, but your accountant can.
|
|
99
|
+
|
|
23
100
|
## Installation
|
|
24
101
|
|
|
25
102
|
```bash
|
package/dist/index.d.mts
CHANGED
|
@@ -875,4 +875,272 @@ declare function createWebhookHandler(options: {
|
|
|
875
875
|
onError?: (error: Error) => void;
|
|
876
876
|
}): (req: any, res: any) => Promise<void>;
|
|
877
877
|
|
|
878
|
-
|
|
878
|
+
/**
|
|
879
|
+
* Inco Lightning Privacy Module
|
|
880
|
+
*
|
|
881
|
+
* Helpers for issuing private receipts with FHE-encrypted payment amounts.
|
|
882
|
+
* Only authorized parties (merchant + customer) can decrypt via Inco covalidators.
|
|
883
|
+
*/
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Inco Lightning Program ID (devnet)
|
|
887
|
+
*/
|
|
888
|
+
declare const INCO_LIGHTNING_PROGRAM_ID: PublicKey;
|
|
889
|
+
/**
|
|
890
|
+
* Settlr Program ID
|
|
891
|
+
*/
|
|
892
|
+
declare const SETTLR_PROGRAM_ID: PublicKey;
|
|
893
|
+
/**
|
|
894
|
+
* Derive the allowance PDA for a given handle and allowed address
|
|
895
|
+
* This PDA stores the decryption permission for a specific address
|
|
896
|
+
*
|
|
897
|
+
* @param handle - The u128 handle to the encrypted value (as bigint)
|
|
898
|
+
* @param allowedAddress - The address being granted decryption access
|
|
899
|
+
* @returns The allowance PDA and bump
|
|
900
|
+
*/
|
|
901
|
+
declare function findAllowancePda(handle: bigint, allowedAddress: PublicKey): [PublicKey, number];
|
|
902
|
+
/**
|
|
903
|
+
* Derive the private receipt PDA for a given payment ID
|
|
904
|
+
*
|
|
905
|
+
* @param paymentId - The payment ID string
|
|
906
|
+
* @returns The private receipt PDA and bump
|
|
907
|
+
*/
|
|
908
|
+
declare function findPrivateReceiptPda(paymentId: string): [PublicKey, number];
|
|
909
|
+
/**
|
|
910
|
+
* Encrypt an amount for Inco Lightning
|
|
911
|
+
*
|
|
912
|
+
* In production, this would use the Inco encryption API to create
|
|
913
|
+
* a proper FHE ciphertext. For now, this is a placeholder that
|
|
914
|
+
* would be replaced with the actual Inco client library.
|
|
915
|
+
*
|
|
916
|
+
* @param amount - The amount in USDC lamports (6 decimals)
|
|
917
|
+
* @returns Encrypted ciphertext as Uint8Array
|
|
918
|
+
*/
|
|
919
|
+
declare function encryptAmount(amount: bigint): Promise<Uint8Array>;
|
|
920
|
+
/**
|
|
921
|
+
* Configuration for issuing a private receipt
|
|
922
|
+
*/
|
|
923
|
+
interface PrivateReceiptConfig {
|
|
924
|
+
/** Payment ID (must be unique) */
|
|
925
|
+
paymentId: string;
|
|
926
|
+
/** Amount in USDC (will be converted to lamports) */
|
|
927
|
+
amount: number;
|
|
928
|
+
/** Customer wallet address (payer and signer) */
|
|
929
|
+
customer: PublicKey;
|
|
930
|
+
/** Merchant wallet address (receives decryption access) */
|
|
931
|
+
merchant: PublicKey;
|
|
932
|
+
/** Pre-computed encrypted amount ciphertext (optional, will encrypt if not provided) */
|
|
933
|
+
encryptedAmount?: Uint8Array;
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Build accounts needed for issuing a private receipt
|
|
937
|
+
*
|
|
938
|
+
* Note: This returns the accounts structure but the actual transaction
|
|
939
|
+
* must be built using the Anchor program client with `remainingAccounts`
|
|
940
|
+
* for the allowance PDAs.
|
|
941
|
+
*
|
|
942
|
+
* @param config - Private receipt configuration
|
|
943
|
+
* @returns Object with all required account addresses
|
|
944
|
+
*/
|
|
945
|
+
declare function buildPrivateReceiptAccounts(config: PrivateReceiptConfig): Promise<{
|
|
946
|
+
customer: PublicKey;
|
|
947
|
+
merchant: PublicKey;
|
|
948
|
+
privateReceipt: PublicKey;
|
|
949
|
+
incoLightningProgram: PublicKey;
|
|
950
|
+
systemProgram: PublicKey;
|
|
951
|
+
bump: number;
|
|
952
|
+
}>;
|
|
953
|
+
/**
|
|
954
|
+
* Simulate a transaction to get the resulting encrypted handle
|
|
955
|
+
*
|
|
956
|
+
* This is needed because we need the handle to derive allowance PDAs,
|
|
957
|
+
* but the handle is only known after the encryption CPI call.
|
|
958
|
+
*
|
|
959
|
+
* Pattern:
|
|
960
|
+
* 1. Build tx without allowance accounts
|
|
961
|
+
* 2. Simulate to get the handle from account state
|
|
962
|
+
* 3. Derive allowance PDAs from handle
|
|
963
|
+
* 4. Execute real tx with allowance accounts in remainingAccounts
|
|
964
|
+
*
|
|
965
|
+
* @param connection - Solana connection
|
|
966
|
+
* @param transaction - Built transaction without allowance accounts
|
|
967
|
+
* @param privateReceiptPda - The PDA where encrypted handle will be stored
|
|
968
|
+
* @returns The encrypted handle as bigint, or null if simulation failed
|
|
969
|
+
*/
|
|
970
|
+
declare function simulateAndGetHandle(connection: any, // Connection type
|
|
971
|
+
transaction: any, // Transaction type
|
|
972
|
+
privateReceiptPda: PublicKey): Promise<bigint | null>;
|
|
973
|
+
/**
|
|
974
|
+
* Build remaining accounts array for allowance PDAs
|
|
975
|
+
*
|
|
976
|
+
* These must be passed to the instruction after deriving from the handle.
|
|
977
|
+
* Since we don't know the handle until after simulation, this is called
|
|
978
|
+
* after simulateAndGetHandle.
|
|
979
|
+
*
|
|
980
|
+
* @param handle - The encrypted handle from simulation
|
|
981
|
+
* @param customer - Customer address (granted access)
|
|
982
|
+
* @param merchant - Merchant address (granted access)
|
|
983
|
+
* @returns Array of remaining accounts for the instruction
|
|
984
|
+
*/
|
|
985
|
+
declare function buildAllowanceRemainingAccounts(handle: bigint, customer: PublicKey, merchant: PublicKey): Array<{
|
|
986
|
+
pubkey: PublicKey;
|
|
987
|
+
isSigner: boolean;
|
|
988
|
+
isWritable: boolean;
|
|
989
|
+
}>;
|
|
990
|
+
/**
|
|
991
|
+
* Full flow example for issuing a private receipt
|
|
992
|
+
*
|
|
993
|
+
* @example
|
|
994
|
+
* ```typescript
|
|
995
|
+
* import { issuePrivateReceiptFlow } from '@settlr/sdk/privacy';
|
|
996
|
+
*
|
|
997
|
+
* const result = await issuePrivateReceiptFlow({
|
|
998
|
+
* connection,
|
|
999
|
+
* program, // Anchor program instance
|
|
1000
|
+
* paymentId: 'payment_123',
|
|
1001
|
+
* amount: 99.99,
|
|
1002
|
+
* customer: customerWallet.publicKey,
|
|
1003
|
+
* merchant: merchantPubkey,
|
|
1004
|
+
* signTransaction: customerWallet.signTransaction,
|
|
1005
|
+
* });
|
|
1006
|
+
*
|
|
1007
|
+
* console.log('Private receipt:', result.signature);
|
|
1008
|
+
* console.log('Handle:', result.handle.toString());
|
|
1009
|
+
* ```
|
|
1010
|
+
*/
|
|
1011
|
+
interface IssuePrivateReceiptResult {
|
|
1012
|
+
/** Transaction signature */
|
|
1013
|
+
signature: string;
|
|
1014
|
+
/** Encrypted amount handle (u128 as bigint) */
|
|
1015
|
+
handle: bigint;
|
|
1016
|
+
/** Private receipt PDA address */
|
|
1017
|
+
privateReceiptPda: PublicKey;
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Privacy-preserving receipt features
|
|
1021
|
+
*
|
|
1022
|
+
* Key benefits:
|
|
1023
|
+
* - Payment amounts hidden on-chain (only u128 handle visible)
|
|
1024
|
+
* - Merchant can still decrypt for accounting/tax compliance
|
|
1025
|
+
* - Customer can verify their payment privately
|
|
1026
|
+
* - Competitors can't see your revenue on-chain
|
|
1027
|
+
*/
|
|
1028
|
+
declare const PrivacyFeatures: {
|
|
1029
|
+
/** Amount is FHE-encrypted, only handle stored on-chain */
|
|
1030
|
+
readonly ENCRYPTED_AMOUNTS: true;
|
|
1031
|
+
/** Selective disclosure - only merchant + customer can decrypt */
|
|
1032
|
+
readonly ACCESS_CONTROL: true;
|
|
1033
|
+
/** CSV export still works (decrypts server-side for authorized merchant) */
|
|
1034
|
+
readonly ACCOUNTING_COMPATIBLE: true;
|
|
1035
|
+
/** Inco covalidators ensure trustless decryption */
|
|
1036
|
+
readonly TRUSTLESS_DECRYPTION: true;
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* One-Click Payments Module
|
|
1041
|
+
*
|
|
1042
|
+
* Enables frictionless repeat payments for returning customers.
|
|
1043
|
+
* Customer approves a spending limit once, merchant can charge without interaction.
|
|
1044
|
+
*/
|
|
1045
|
+
interface SpendingApproval {
|
|
1046
|
+
id: string;
|
|
1047
|
+
customerWallet: string;
|
|
1048
|
+
customerEmail?: string;
|
|
1049
|
+
merchantWallet: string;
|
|
1050
|
+
spendingLimit: number;
|
|
1051
|
+
amountSpent: number;
|
|
1052
|
+
remainingLimit: number;
|
|
1053
|
+
expiresAt: Date;
|
|
1054
|
+
status: 'active' | 'expired' | 'revoked';
|
|
1055
|
+
createdAt: Date;
|
|
1056
|
+
}
|
|
1057
|
+
interface ApproveOneClickOptions {
|
|
1058
|
+
/** Customer's wallet address */
|
|
1059
|
+
customerWallet: string;
|
|
1060
|
+
/** Customer's email (optional, for notifications) */
|
|
1061
|
+
customerEmail?: string;
|
|
1062
|
+
/** Merchant's wallet address */
|
|
1063
|
+
merchantWallet: string;
|
|
1064
|
+
/** Maximum USDC amount the merchant can charge */
|
|
1065
|
+
spendingLimit: number;
|
|
1066
|
+
/** Days until approval expires (default: 30) */
|
|
1067
|
+
expiresInDays?: number;
|
|
1068
|
+
}
|
|
1069
|
+
interface ChargeOneClickOptions {
|
|
1070
|
+
/** Customer's wallet address */
|
|
1071
|
+
customerWallet: string;
|
|
1072
|
+
/** Merchant's wallet address */
|
|
1073
|
+
merchantWallet: string;
|
|
1074
|
+
/** Amount to charge in USDC */
|
|
1075
|
+
amount: number;
|
|
1076
|
+
/** Optional memo for the transaction */
|
|
1077
|
+
memo?: string;
|
|
1078
|
+
}
|
|
1079
|
+
interface OneClickResult {
|
|
1080
|
+
success: boolean;
|
|
1081
|
+
error?: string;
|
|
1082
|
+
txSignature?: string;
|
|
1083
|
+
remainingLimit?: number;
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* One-Click Payment Client
|
|
1087
|
+
*
|
|
1088
|
+
* @example
|
|
1089
|
+
* ```typescript
|
|
1090
|
+
* import { OneClickClient } from '@settlr/sdk';
|
|
1091
|
+
*
|
|
1092
|
+
* const oneClick = new OneClickClient('https://settlr.dev');
|
|
1093
|
+
*
|
|
1094
|
+
* // Customer approves merchant
|
|
1095
|
+
* await oneClick.approve({
|
|
1096
|
+
* customerWallet: 'Ac52MM...',
|
|
1097
|
+
* merchantWallet: 'DjLFeM...',
|
|
1098
|
+
* spendingLimit: 100, // $100 max
|
|
1099
|
+
* });
|
|
1100
|
+
*
|
|
1101
|
+
* // Merchant charges customer later (no interaction needed)
|
|
1102
|
+
* const result = await oneClick.charge({
|
|
1103
|
+
* customerWallet: 'Ac52MM...',
|
|
1104
|
+
* merchantWallet: 'DjLFeM...',
|
|
1105
|
+
* amount: 25,
|
|
1106
|
+
* });
|
|
1107
|
+
* ```
|
|
1108
|
+
*/
|
|
1109
|
+
declare class OneClickClient {
|
|
1110
|
+
private baseUrl;
|
|
1111
|
+
constructor(baseUrl?: string);
|
|
1112
|
+
/**
|
|
1113
|
+
* Customer approves a spending limit for a merchant
|
|
1114
|
+
*/
|
|
1115
|
+
approve(options: ApproveOneClickOptions): Promise<{
|
|
1116
|
+
success: boolean;
|
|
1117
|
+
approval?: SpendingApproval;
|
|
1118
|
+
}>;
|
|
1119
|
+
/**
|
|
1120
|
+
* Check if customer has active approval for merchant
|
|
1121
|
+
*/
|
|
1122
|
+
check(customerWallet: string, merchantWallet: string): Promise<{
|
|
1123
|
+
hasApproval: boolean;
|
|
1124
|
+
remainingLimit?: number;
|
|
1125
|
+
approval?: SpendingApproval;
|
|
1126
|
+
}>;
|
|
1127
|
+
/**
|
|
1128
|
+
* Merchant charges customer using their one-click approval
|
|
1129
|
+
* No customer interaction required if approval exists with sufficient limit
|
|
1130
|
+
*/
|
|
1131
|
+
charge(options: ChargeOneClickOptions): Promise<OneClickResult>;
|
|
1132
|
+
/**
|
|
1133
|
+
* Customer revokes merchant's one-click access
|
|
1134
|
+
*/
|
|
1135
|
+
revoke(customerWallet: string, merchantWallet: string): Promise<{
|
|
1136
|
+
success: boolean;
|
|
1137
|
+
}>;
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Create a one-click payment client
|
|
1141
|
+
*
|
|
1142
|
+
* @param baseUrl - Settlr API base URL (default: https://settlr.dev)
|
|
1143
|
+
*/
|
|
1144
|
+
declare function createOneClickClient(baseUrl?: string): OneClickClient;
|
|
1145
|
+
|
|
1146
|
+
export { type ApproveOneClickOptions, BuyButton, type BuyButtonProps, type ChargeOneClickOptions, CheckoutWidget, type CheckoutWidgetProps, type CreatePaymentOptions, type CreateSubscriptionOptions, INCO_LIGHTNING_PROGRAM_ID, type IssuePrivateReceiptResult, type MerchantConfig, OneClickClient, type OneClickResult, type Payment, PaymentModal, type PaymentModalProps, type PaymentResult, type PaymentStatus, PrivacyFeatures, type PrivateReceiptConfig, SETTLR_CHECKOUT_URL, SETTLR_PROGRAM_ID, SUPPORTED_NETWORKS, SUPPORTED_TOKENS, Settlr, type SettlrConfig, SettlrProvider, type SpendingApproval, type Subscription, type SubscriptionInterval, type SubscriptionPlan, type SubscriptionStatus, type SupportedToken, type TransactionOptions, USDC_MINT_DEVNET, USDC_MINT_MAINNET, USDT_MINT_DEVNET, USDT_MINT_MAINNET, type WebhookEventType, type WebhookHandler, type WebhookHandlers, type WebhookPayload, buildAllowanceRemainingAccounts, buildPrivateReceiptAccounts, createOneClickClient, createWebhookHandler, encryptAmount, findAllowancePda, findPrivateReceiptPda, formatUSDC, getTokenDecimals, getTokenMint, parseUSDC, parseWebhookPayload, shortenAddress, simulateAndGetHandle, usePaymentLink, usePaymentModal, useSettlr, verifyWebhookSignature };
|
package/dist/index.d.ts
CHANGED
|
@@ -875,4 +875,272 @@ declare function createWebhookHandler(options: {
|
|
|
875
875
|
onError?: (error: Error) => void;
|
|
876
876
|
}): (req: any, res: any) => Promise<void>;
|
|
877
877
|
|
|
878
|
-
|
|
878
|
+
/**
|
|
879
|
+
* Inco Lightning Privacy Module
|
|
880
|
+
*
|
|
881
|
+
* Helpers for issuing private receipts with FHE-encrypted payment amounts.
|
|
882
|
+
* Only authorized parties (merchant + customer) can decrypt via Inco covalidators.
|
|
883
|
+
*/
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Inco Lightning Program ID (devnet)
|
|
887
|
+
*/
|
|
888
|
+
declare const INCO_LIGHTNING_PROGRAM_ID: PublicKey;
|
|
889
|
+
/**
|
|
890
|
+
* Settlr Program ID
|
|
891
|
+
*/
|
|
892
|
+
declare const SETTLR_PROGRAM_ID: PublicKey;
|
|
893
|
+
/**
|
|
894
|
+
* Derive the allowance PDA for a given handle and allowed address
|
|
895
|
+
* This PDA stores the decryption permission for a specific address
|
|
896
|
+
*
|
|
897
|
+
* @param handle - The u128 handle to the encrypted value (as bigint)
|
|
898
|
+
* @param allowedAddress - The address being granted decryption access
|
|
899
|
+
* @returns The allowance PDA and bump
|
|
900
|
+
*/
|
|
901
|
+
declare function findAllowancePda(handle: bigint, allowedAddress: PublicKey): [PublicKey, number];
|
|
902
|
+
/**
|
|
903
|
+
* Derive the private receipt PDA for a given payment ID
|
|
904
|
+
*
|
|
905
|
+
* @param paymentId - The payment ID string
|
|
906
|
+
* @returns The private receipt PDA and bump
|
|
907
|
+
*/
|
|
908
|
+
declare function findPrivateReceiptPda(paymentId: string): [PublicKey, number];
|
|
909
|
+
/**
|
|
910
|
+
* Encrypt an amount for Inco Lightning
|
|
911
|
+
*
|
|
912
|
+
* In production, this would use the Inco encryption API to create
|
|
913
|
+
* a proper FHE ciphertext. For now, this is a placeholder that
|
|
914
|
+
* would be replaced with the actual Inco client library.
|
|
915
|
+
*
|
|
916
|
+
* @param amount - The amount in USDC lamports (6 decimals)
|
|
917
|
+
* @returns Encrypted ciphertext as Uint8Array
|
|
918
|
+
*/
|
|
919
|
+
declare function encryptAmount(amount: bigint): Promise<Uint8Array>;
|
|
920
|
+
/**
|
|
921
|
+
* Configuration for issuing a private receipt
|
|
922
|
+
*/
|
|
923
|
+
interface PrivateReceiptConfig {
|
|
924
|
+
/** Payment ID (must be unique) */
|
|
925
|
+
paymentId: string;
|
|
926
|
+
/** Amount in USDC (will be converted to lamports) */
|
|
927
|
+
amount: number;
|
|
928
|
+
/** Customer wallet address (payer and signer) */
|
|
929
|
+
customer: PublicKey;
|
|
930
|
+
/** Merchant wallet address (receives decryption access) */
|
|
931
|
+
merchant: PublicKey;
|
|
932
|
+
/** Pre-computed encrypted amount ciphertext (optional, will encrypt if not provided) */
|
|
933
|
+
encryptedAmount?: Uint8Array;
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Build accounts needed for issuing a private receipt
|
|
937
|
+
*
|
|
938
|
+
* Note: This returns the accounts structure but the actual transaction
|
|
939
|
+
* must be built using the Anchor program client with `remainingAccounts`
|
|
940
|
+
* for the allowance PDAs.
|
|
941
|
+
*
|
|
942
|
+
* @param config - Private receipt configuration
|
|
943
|
+
* @returns Object with all required account addresses
|
|
944
|
+
*/
|
|
945
|
+
declare function buildPrivateReceiptAccounts(config: PrivateReceiptConfig): Promise<{
|
|
946
|
+
customer: PublicKey;
|
|
947
|
+
merchant: PublicKey;
|
|
948
|
+
privateReceipt: PublicKey;
|
|
949
|
+
incoLightningProgram: PublicKey;
|
|
950
|
+
systemProgram: PublicKey;
|
|
951
|
+
bump: number;
|
|
952
|
+
}>;
|
|
953
|
+
/**
|
|
954
|
+
* Simulate a transaction to get the resulting encrypted handle
|
|
955
|
+
*
|
|
956
|
+
* This is needed because we need the handle to derive allowance PDAs,
|
|
957
|
+
* but the handle is only known after the encryption CPI call.
|
|
958
|
+
*
|
|
959
|
+
* Pattern:
|
|
960
|
+
* 1. Build tx without allowance accounts
|
|
961
|
+
* 2. Simulate to get the handle from account state
|
|
962
|
+
* 3. Derive allowance PDAs from handle
|
|
963
|
+
* 4. Execute real tx with allowance accounts in remainingAccounts
|
|
964
|
+
*
|
|
965
|
+
* @param connection - Solana connection
|
|
966
|
+
* @param transaction - Built transaction without allowance accounts
|
|
967
|
+
* @param privateReceiptPda - The PDA where encrypted handle will be stored
|
|
968
|
+
* @returns The encrypted handle as bigint, or null if simulation failed
|
|
969
|
+
*/
|
|
970
|
+
declare function simulateAndGetHandle(connection: any, // Connection type
|
|
971
|
+
transaction: any, // Transaction type
|
|
972
|
+
privateReceiptPda: PublicKey): Promise<bigint | null>;
|
|
973
|
+
/**
|
|
974
|
+
* Build remaining accounts array for allowance PDAs
|
|
975
|
+
*
|
|
976
|
+
* These must be passed to the instruction after deriving from the handle.
|
|
977
|
+
* Since we don't know the handle until after simulation, this is called
|
|
978
|
+
* after simulateAndGetHandle.
|
|
979
|
+
*
|
|
980
|
+
* @param handle - The encrypted handle from simulation
|
|
981
|
+
* @param customer - Customer address (granted access)
|
|
982
|
+
* @param merchant - Merchant address (granted access)
|
|
983
|
+
* @returns Array of remaining accounts for the instruction
|
|
984
|
+
*/
|
|
985
|
+
declare function buildAllowanceRemainingAccounts(handle: bigint, customer: PublicKey, merchant: PublicKey): Array<{
|
|
986
|
+
pubkey: PublicKey;
|
|
987
|
+
isSigner: boolean;
|
|
988
|
+
isWritable: boolean;
|
|
989
|
+
}>;
|
|
990
|
+
/**
|
|
991
|
+
* Full flow example for issuing a private receipt
|
|
992
|
+
*
|
|
993
|
+
* @example
|
|
994
|
+
* ```typescript
|
|
995
|
+
* import { issuePrivateReceiptFlow } from '@settlr/sdk/privacy';
|
|
996
|
+
*
|
|
997
|
+
* const result = await issuePrivateReceiptFlow({
|
|
998
|
+
* connection,
|
|
999
|
+
* program, // Anchor program instance
|
|
1000
|
+
* paymentId: 'payment_123',
|
|
1001
|
+
* amount: 99.99,
|
|
1002
|
+
* customer: customerWallet.publicKey,
|
|
1003
|
+
* merchant: merchantPubkey,
|
|
1004
|
+
* signTransaction: customerWallet.signTransaction,
|
|
1005
|
+
* });
|
|
1006
|
+
*
|
|
1007
|
+
* console.log('Private receipt:', result.signature);
|
|
1008
|
+
* console.log('Handle:', result.handle.toString());
|
|
1009
|
+
* ```
|
|
1010
|
+
*/
|
|
1011
|
+
interface IssuePrivateReceiptResult {
|
|
1012
|
+
/** Transaction signature */
|
|
1013
|
+
signature: string;
|
|
1014
|
+
/** Encrypted amount handle (u128 as bigint) */
|
|
1015
|
+
handle: bigint;
|
|
1016
|
+
/** Private receipt PDA address */
|
|
1017
|
+
privateReceiptPda: PublicKey;
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Privacy-preserving receipt features
|
|
1021
|
+
*
|
|
1022
|
+
* Key benefits:
|
|
1023
|
+
* - Payment amounts hidden on-chain (only u128 handle visible)
|
|
1024
|
+
* - Merchant can still decrypt for accounting/tax compliance
|
|
1025
|
+
* - Customer can verify their payment privately
|
|
1026
|
+
* - Competitors can't see your revenue on-chain
|
|
1027
|
+
*/
|
|
1028
|
+
declare const PrivacyFeatures: {
|
|
1029
|
+
/** Amount is FHE-encrypted, only handle stored on-chain */
|
|
1030
|
+
readonly ENCRYPTED_AMOUNTS: true;
|
|
1031
|
+
/** Selective disclosure - only merchant + customer can decrypt */
|
|
1032
|
+
readonly ACCESS_CONTROL: true;
|
|
1033
|
+
/** CSV export still works (decrypts server-side for authorized merchant) */
|
|
1034
|
+
readonly ACCOUNTING_COMPATIBLE: true;
|
|
1035
|
+
/** Inco covalidators ensure trustless decryption */
|
|
1036
|
+
readonly TRUSTLESS_DECRYPTION: true;
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* One-Click Payments Module
|
|
1041
|
+
*
|
|
1042
|
+
* Enables frictionless repeat payments for returning customers.
|
|
1043
|
+
* Customer approves a spending limit once, merchant can charge without interaction.
|
|
1044
|
+
*/
|
|
1045
|
+
interface SpendingApproval {
|
|
1046
|
+
id: string;
|
|
1047
|
+
customerWallet: string;
|
|
1048
|
+
customerEmail?: string;
|
|
1049
|
+
merchantWallet: string;
|
|
1050
|
+
spendingLimit: number;
|
|
1051
|
+
amountSpent: number;
|
|
1052
|
+
remainingLimit: number;
|
|
1053
|
+
expiresAt: Date;
|
|
1054
|
+
status: 'active' | 'expired' | 'revoked';
|
|
1055
|
+
createdAt: Date;
|
|
1056
|
+
}
|
|
1057
|
+
interface ApproveOneClickOptions {
|
|
1058
|
+
/** Customer's wallet address */
|
|
1059
|
+
customerWallet: string;
|
|
1060
|
+
/** Customer's email (optional, for notifications) */
|
|
1061
|
+
customerEmail?: string;
|
|
1062
|
+
/** Merchant's wallet address */
|
|
1063
|
+
merchantWallet: string;
|
|
1064
|
+
/** Maximum USDC amount the merchant can charge */
|
|
1065
|
+
spendingLimit: number;
|
|
1066
|
+
/** Days until approval expires (default: 30) */
|
|
1067
|
+
expiresInDays?: number;
|
|
1068
|
+
}
|
|
1069
|
+
interface ChargeOneClickOptions {
|
|
1070
|
+
/** Customer's wallet address */
|
|
1071
|
+
customerWallet: string;
|
|
1072
|
+
/** Merchant's wallet address */
|
|
1073
|
+
merchantWallet: string;
|
|
1074
|
+
/** Amount to charge in USDC */
|
|
1075
|
+
amount: number;
|
|
1076
|
+
/** Optional memo for the transaction */
|
|
1077
|
+
memo?: string;
|
|
1078
|
+
}
|
|
1079
|
+
interface OneClickResult {
|
|
1080
|
+
success: boolean;
|
|
1081
|
+
error?: string;
|
|
1082
|
+
txSignature?: string;
|
|
1083
|
+
remainingLimit?: number;
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* One-Click Payment Client
|
|
1087
|
+
*
|
|
1088
|
+
* @example
|
|
1089
|
+
* ```typescript
|
|
1090
|
+
* import { OneClickClient } from '@settlr/sdk';
|
|
1091
|
+
*
|
|
1092
|
+
* const oneClick = new OneClickClient('https://settlr.dev');
|
|
1093
|
+
*
|
|
1094
|
+
* // Customer approves merchant
|
|
1095
|
+
* await oneClick.approve({
|
|
1096
|
+
* customerWallet: 'Ac52MM...',
|
|
1097
|
+
* merchantWallet: 'DjLFeM...',
|
|
1098
|
+
* spendingLimit: 100, // $100 max
|
|
1099
|
+
* });
|
|
1100
|
+
*
|
|
1101
|
+
* // Merchant charges customer later (no interaction needed)
|
|
1102
|
+
* const result = await oneClick.charge({
|
|
1103
|
+
* customerWallet: 'Ac52MM...',
|
|
1104
|
+
* merchantWallet: 'DjLFeM...',
|
|
1105
|
+
* amount: 25,
|
|
1106
|
+
* });
|
|
1107
|
+
* ```
|
|
1108
|
+
*/
|
|
1109
|
+
declare class OneClickClient {
|
|
1110
|
+
private baseUrl;
|
|
1111
|
+
constructor(baseUrl?: string);
|
|
1112
|
+
/**
|
|
1113
|
+
* Customer approves a spending limit for a merchant
|
|
1114
|
+
*/
|
|
1115
|
+
approve(options: ApproveOneClickOptions): Promise<{
|
|
1116
|
+
success: boolean;
|
|
1117
|
+
approval?: SpendingApproval;
|
|
1118
|
+
}>;
|
|
1119
|
+
/**
|
|
1120
|
+
* Check if customer has active approval for merchant
|
|
1121
|
+
*/
|
|
1122
|
+
check(customerWallet: string, merchantWallet: string): Promise<{
|
|
1123
|
+
hasApproval: boolean;
|
|
1124
|
+
remainingLimit?: number;
|
|
1125
|
+
approval?: SpendingApproval;
|
|
1126
|
+
}>;
|
|
1127
|
+
/**
|
|
1128
|
+
* Merchant charges customer using their one-click approval
|
|
1129
|
+
* No customer interaction required if approval exists with sufficient limit
|
|
1130
|
+
*/
|
|
1131
|
+
charge(options: ChargeOneClickOptions): Promise<OneClickResult>;
|
|
1132
|
+
/**
|
|
1133
|
+
* Customer revokes merchant's one-click access
|
|
1134
|
+
*/
|
|
1135
|
+
revoke(customerWallet: string, merchantWallet: string): Promise<{
|
|
1136
|
+
success: boolean;
|
|
1137
|
+
}>;
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Create a one-click payment client
|
|
1141
|
+
*
|
|
1142
|
+
* @param baseUrl - Settlr API base URL (default: https://settlr.dev)
|
|
1143
|
+
*/
|
|
1144
|
+
declare function createOneClickClient(baseUrl?: string): OneClickClient;
|
|
1145
|
+
|
|
1146
|
+
export { type ApproveOneClickOptions, BuyButton, type BuyButtonProps, type ChargeOneClickOptions, CheckoutWidget, type CheckoutWidgetProps, type CreatePaymentOptions, type CreateSubscriptionOptions, INCO_LIGHTNING_PROGRAM_ID, type IssuePrivateReceiptResult, type MerchantConfig, OneClickClient, type OneClickResult, type Payment, PaymentModal, type PaymentModalProps, type PaymentResult, type PaymentStatus, PrivacyFeatures, type PrivateReceiptConfig, SETTLR_CHECKOUT_URL, SETTLR_PROGRAM_ID, SUPPORTED_NETWORKS, SUPPORTED_TOKENS, Settlr, type SettlrConfig, SettlrProvider, type SpendingApproval, type Subscription, type SubscriptionInterval, type SubscriptionPlan, type SubscriptionStatus, type SupportedToken, type TransactionOptions, USDC_MINT_DEVNET, USDC_MINT_MAINNET, USDT_MINT_DEVNET, USDT_MINT_MAINNET, type WebhookEventType, type WebhookHandler, type WebhookHandlers, type WebhookPayload, buildAllowanceRemainingAccounts, buildPrivateReceiptAccounts, createOneClickClient, createWebhookHandler, encryptAmount, findAllowancePda, findPrivateReceiptPda, formatUSDC, getTokenDecimals, getTokenMint, parseUSDC, parseWebhookPayload, shortenAddress, simulateAndGetHandle, usePaymentLink, usePaymentModal, useSettlr, verifyWebhookSignature };
|
package/dist/index.js
CHANGED
|
@@ -32,8 +32,12 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
BuyButton: () => BuyButton,
|
|
34
34
|
CheckoutWidget: () => CheckoutWidget,
|
|
35
|
+
INCO_LIGHTNING_PROGRAM_ID: () => INCO_LIGHTNING_PROGRAM_ID,
|
|
36
|
+
OneClickClient: () => OneClickClient,
|
|
35
37
|
PaymentModal: () => PaymentModal,
|
|
38
|
+
PrivacyFeatures: () => PrivacyFeatures,
|
|
36
39
|
SETTLR_CHECKOUT_URL: () => SETTLR_CHECKOUT_URL,
|
|
40
|
+
SETTLR_PROGRAM_ID: () => SETTLR_PROGRAM_ID,
|
|
37
41
|
SUPPORTED_NETWORKS: () => SUPPORTED_NETWORKS,
|
|
38
42
|
SUPPORTED_TOKENS: () => SUPPORTED_TOKENS,
|
|
39
43
|
Settlr: () => Settlr,
|
|
@@ -42,13 +46,20 @@ __export(index_exports, {
|
|
|
42
46
|
USDC_MINT_MAINNET: () => USDC_MINT_MAINNET,
|
|
43
47
|
USDT_MINT_DEVNET: () => USDT_MINT_DEVNET,
|
|
44
48
|
USDT_MINT_MAINNET: () => USDT_MINT_MAINNET,
|
|
49
|
+
buildAllowanceRemainingAccounts: () => buildAllowanceRemainingAccounts,
|
|
50
|
+
buildPrivateReceiptAccounts: () => buildPrivateReceiptAccounts,
|
|
51
|
+
createOneClickClient: () => createOneClickClient,
|
|
45
52
|
createWebhookHandler: () => createWebhookHandler,
|
|
53
|
+
encryptAmount: () => encryptAmount,
|
|
54
|
+
findAllowancePda: () => findAllowancePda,
|
|
55
|
+
findPrivateReceiptPda: () => findPrivateReceiptPda,
|
|
46
56
|
formatUSDC: () => formatUSDC,
|
|
47
57
|
getTokenDecimals: () => getTokenDecimals,
|
|
48
58
|
getTokenMint: () => getTokenMint,
|
|
49
59
|
parseUSDC: () => parseUSDC,
|
|
50
60
|
parseWebhookPayload: () => parseWebhookPayload,
|
|
51
61
|
shortenAddress: () => shortenAddress,
|
|
62
|
+
simulateAndGetHandle: () => simulateAndGetHandle,
|
|
52
63
|
usePaymentLink: () => usePaymentLink,
|
|
53
64
|
usePaymentModal: () => usePaymentModal,
|
|
54
65
|
useSettlr: () => useSettlr,
|
|
@@ -1219,12 +1230,212 @@ function createWebhookHandler(options) {
|
|
|
1219
1230
|
}
|
|
1220
1231
|
};
|
|
1221
1232
|
}
|
|
1233
|
+
|
|
1234
|
+
// src/privacy.ts
|
|
1235
|
+
var import_web33 = require("@solana/web3.js");
|
|
1236
|
+
var INCO_LIGHTNING_PROGRAM_ID = new import_web33.PublicKey(
|
|
1237
|
+
"5sjEbPiqgZrYwR31ahR6Uk9wf5awoX61YGg7jExQSwaj"
|
|
1238
|
+
);
|
|
1239
|
+
var SETTLR_PROGRAM_ID = new import_web33.PublicKey(
|
|
1240
|
+
"339A4zncMj8fbM2zvEopYXu6TZqRieJKebDiXCKwquA5"
|
|
1241
|
+
);
|
|
1242
|
+
function findAllowancePda(handle, allowedAddress) {
|
|
1243
|
+
const handleBuffer = Buffer.alloc(16);
|
|
1244
|
+
let h = handle;
|
|
1245
|
+
for (let i = 0; i < 16; i++) {
|
|
1246
|
+
handleBuffer[i] = Number(h & BigInt(255));
|
|
1247
|
+
h = h >> BigInt(8);
|
|
1248
|
+
}
|
|
1249
|
+
return import_web33.PublicKey.findProgramAddressSync(
|
|
1250
|
+
[handleBuffer, allowedAddress.toBuffer()],
|
|
1251
|
+
INCO_LIGHTNING_PROGRAM_ID
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
function findPrivateReceiptPda(paymentId) {
|
|
1255
|
+
return import_web33.PublicKey.findProgramAddressSync(
|
|
1256
|
+
[Buffer.from("private_receipt"), Buffer.from(paymentId)],
|
|
1257
|
+
SETTLR_PROGRAM_ID
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1260
|
+
async function encryptAmount(amount) {
|
|
1261
|
+
const buffer = Buffer.alloc(16);
|
|
1262
|
+
let a = amount;
|
|
1263
|
+
for (let i = 0; i < 16; i++) {
|
|
1264
|
+
buffer[i] = Number(a & BigInt(255));
|
|
1265
|
+
a = a >> BigInt(8);
|
|
1266
|
+
}
|
|
1267
|
+
return new Uint8Array(buffer);
|
|
1268
|
+
}
|
|
1269
|
+
async function buildPrivateReceiptAccounts(config) {
|
|
1270
|
+
const [privateReceiptPda, privateReceiptBump] = findPrivateReceiptPda(config.paymentId);
|
|
1271
|
+
return {
|
|
1272
|
+
customer: config.customer,
|
|
1273
|
+
merchant: config.merchant,
|
|
1274
|
+
privateReceipt: privateReceiptPda,
|
|
1275
|
+
incoLightningProgram: INCO_LIGHTNING_PROGRAM_ID,
|
|
1276
|
+
systemProgram: import_web33.SystemProgram.programId,
|
|
1277
|
+
bump: privateReceiptBump
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
async function simulateAndGetHandle(connection, transaction, privateReceiptPda) {
|
|
1281
|
+
try {
|
|
1282
|
+
const simulation = await connection.simulateTransaction(
|
|
1283
|
+
transaction,
|
|
1284
|
+
void 0,
|
|
1285
|
+
[privateReceiptPda]
|
|
1286
|
+
);
|
|
1287
|
+
if (simulation.value.err) {
|
|
1288
|
+
console.error("Simulation failed:", simulation.value.err);
|
|
1289
|
+
return null;
|
|
1290
|
+
}
|
|
1291
|
+
if (simulation.value.accounts?.[0]?.data) {
|
|
1292
|
+
const data = Buffer.from(simulation.value.accounts[0].data[0], "base64");
|
|
1293
|
+
const paymentIdLen = data.readUInt32LE(8);
|
|
1294
|
+
const handleOffset = 8 + 4 + paymentIdLen + 32 + 32;
|
|
1295
|
+
let handle = BigInt(0);
|
|
1296
|
+
for (let i = 15; i >= 0; i--) {
|
|
1297
|
+
handle = handle * BigInt(256) + BigInt(data[handleOffset + i]);
|
|
1298
|
+
}
|
|
1299
|
+
return handle;
|
|
1300
|
+
}
|
|
1301
|
+
return null;
|
|
1302
|
+
} catch (error) {
|
|
1303
|
+
console.error("Simulation error:", error);
|
|
1304
|
+
return null;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
function buildAllowanceRemainingAccounts(handle, customer, merchant) {
|
|
1308
|
+
const [customerAllowancePda] = findAllowancePda(handle, customer);
|
|
1309
|
+
const [merchantAllowancePda] = findAllowancePda(handle, merchant);
|
|
1310
|
+
return [
|
|
1311
|
+
{ pubkey: customerAllowancePda, isSigner: false, isWritable: true },
|
|
1312
|
+
{ pubkey: merchantAllowancePda, isSigner: false, isWritable: true }
|
|
1313
|
+
];
|
|
1314
|
+
}
|
|
1315
|
+
var PrivacyFeatures = {
|
|
1316
|
+
/** Amount is FHE-encrypted, only handle stored on-chain */
|
|
1317
|
+
ENCRYPTED_AMOUNTS: true,
|
|
1318
|
+
/** Selective disclosure - only merchant + customer can decrypt */
|
|
1319
|
+
ACCESS_CONTROL: true,
|
|
1320
|
+
/** CSV export still works (decrypts server-side for authorized merchant) */
|
|
1321
|
+
ACCOUNTING_COMPATIBLE: true,
|
|
1322
|
+
/** Inco covalidators ensure trustless decryption */
|
|
1323
|
+
TRUSTLESS_DECRYPTION: true
|
|
1324
|
+
};
|
|
1325
|
+
var BillingCycles = {
|
|
1326
|
+
/** Weekly (7 days) */
|
|
1327
|
+
WEEKLY: 7 * 24 * 60 * 60,
|
|
1328
|
+
/** Bi-weekly (14 days) */
|
|
1329
|
+
BIWEEKLY: 14 * 24 * 60 * 60,
|
|
1330
|
+
/** Monthly (30 days) */
|
|
1331
|
+
MONTHLY: 30 * 24 * 60 * 60,
|
|
1332
|
+
/** Quarterly (90 days) */
|
|
1333
|
+
QUARTERLY: 90 * 24 * 60 * 60,
|
|
1334
|
+
/** Yearly (365 days) */
|
|
1335
|
+
YEARLY: 365 * 24 * 60 * 60
|
|
1336
|
+
};
|
|
1337
|
+
|
|
1338
|
+
// src/one-click.ts
|
|
1339
|
+
var OneClickClient = class {
|
|
1340
|
+
constructor(baseUrl = "https://settlr.dev") {
|
|
1341
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Customer approves a spending limit for a merchant
|
|
1345
|
+
*/
|
|
1346
|
+
async approve(options) {
|
|
1347
|
+
const response = await fetch(`${this.baseUrl}/api/one-click`, {
|
|
1348
|
+
method: "POST",
|
|
1349
|
+
headers: { "Content-Type": "application/json" },
|
|
1350
|
+
body: JSON.stringify({
|
|
1351
|
+
action: "approve",
|
|
1352
|
+
...options
|
|
1353
|
+
})
|
|
1354
|
+
});
|
|
1355
|
+
const data = await response.json();
|
|
1356
|
+
if (!response.ok) {
|
|
1357
|
+
throw new Error(data.error || "Failed to create approval");
|
|
1358
|
+
}
|
|
1359
|
+
return {
|
|
1360
|
+
success: true,
|
|
1361
|
+
approval: data.approval
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Check if customer has active approval for merchant
|
|
1366
|
+
*/
|
|
1367
|
+
async check(customerWallet, merchantWallet) {
|
|
1368
|
+
const response = await fetch(`${this.baseUrl}/api/one-click`, {
|
|
1369
|
+
method: "POST",
|
|
1370
|
+
headers: { "Content-Type": "application/json" },
|
|
1371
|
+
body: JSON.stringify({
|
|
1372
|
+
action: "check",
|
|
1373
|
+
customerWallet,
|
|
1374
|
+
merchantWallet
|
|
1375
|
+
})
|
|
1376
|
+
});
|
|
1377
|
+
const data = await response.json();
|
|
1378
|
+
return {
|
|
1379
|
+
hasApproval: data.hasApproval || false,
|
|
1380
|
+
remainingLimit: data.remainingLimit,
|
|
1381
|
+
approval: data.approval
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
/**
|
|
1385
|
+
* Merchant charges customer using their one-click approval
|
|
1386
|
+
* No customer interaction required if approval exists with sufficient limit
|
|
1387
|
+
*/
|
|
1388
|
+
async charge(options) {
|
|
1389
|
+
const response = await fetch(`${this.baseUrl}/api/one-click`, {
|
|
1390
|
+
method: "POST",
|
|
1391
|
+
headers: { "Content-Type": "application/json" },
|
|
1392
|
+
body: JSON.stringify({
|
|
1393
|
+
action: "charge",
|
|
1394
|
+
...options
|
|
1395
|
+
})
|
|
1396
|
+
});
|
|
1397
|
+
const data = await response.json();
|
|
1398
|
+
if (!response.ok) {
|
|
1399
|
+
return {
|
|
1400
|
+
success: false,
|
|
1401
|
+
error: data.error
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
return {
|
|
1405
|
+
success: true,
|
|
1406
|
+
txSignature: data.txSignature,
|
|
1407
|
+
remainingLimit: data.remainingLimit
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Customer revokes merchant's one-click access
|
|
1412
|
+
*/
|
|
1413
|
+
async revoke(customerWallet, merchantWallet) {
|
|
1414
|
+
const response = await fetch(`${this.baseUrl}/api/one-click`, {
|
|
1415
|
+
method: "POST",
|
|
1416
|
+
headers: { "Content-Type": "application/json" },
|
|
1417
|
+
body: JSON.stringify({
|
|
1418
|
+
action: "revoke",
|
|
1419
|
+
customerWallet,
|
|
1420
|
+
merchantWallet
|
|
1421
|
+
})
|
|
1422
|
+
});
|
|
1423
|
+
return { success: response.ok };
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
function createOneClickClient(baseUrl) {
|
|
1427
|
+
return new OneClickClient(baseUrl);
|
|
1428
|
+
}
|
|
1222
1429
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1223
1430
|
0 && (module.exports = {
|
|
1224
1431
|
BuyButton,
|
|
1225
1432
|
CheckoutWidget,
|
|
1433
|
+
INCO_LIGHTNING_PROGRAM_ID,
|
|
1434
|
+
OneClickClient,
|
|
1226
1435
|
PaymentModal,
|
|
1436
|
+
PrivacyFeatures,
|
|
1227
1437
|
SETTLR_CHECKOUT_URL,
|
|
1438
|
+
SETTLR_PROGRAM_ID,
|
|
1228
1439
|
SUPPORTED_NETWORKS,
|
|
1229
1440
|
SUPPORTED_TOKENS,
|
|
1230
1441
|
Settlr,
|
|
@@ -1233,13 +1444,20 @@ function createWebhookHandler(options) {
|
|
|
1233
1444
|
USDC_MINT_MAINNET,
|
|
1234
1445
|
USDT_MINT_DEVNET,
|
|
1235
1446
|
USDT_MINT_MAINNET,
|
|
1447
|
+
buildAllowanceRemainingAccounts,
|
|
1448
|
+
buildPrivateReceiptAccounts,
|
|
1449
|
+
createOneClickClient,
|
|
1236
1450
|
createWebhookHandler,
|
|
1451
|
+
encryptAmount,
|
|
1452
|
+
findAllowancePda,
|
|
1453
|
+
findPrivateReceiptPda,
|
|
1237
1454
|
formatUSDC,
|
|
1238
1455
|
getTokenDecimals,
|
|
1239
1456
|
getTokenMint,
|
|
1240
1457
|
parseUSDC,
|
|
1241
1458
|
parseWebhookPayload,
|
|
1242
1459
|
shortenAddress,
|
|
1460
|
+
simulateAndGetHandle,
|
|
1243
1461
|
usePaymentLink,
|
|
1244
1462
|
usePaymentModal,
|
|
1245
1463
|
useSettlr,
|
package/dist/index.mjs
CHANGED
|
@@ -1176,11 +1176,211 @@ function createWebhookHandler(options) {
|
|
|
1176
1176
|
}
|
|
1177
1177
|
};
|
|
1178
1178
|
}
|
|
1179
|
+
|
|
1180
|
+
// src/privacy.ts
|
|
1181
|
+
import { PublicKey as PublicKey3, SystemProgram } from "@solana/web3.js";
|
|
1182
|
+
var INCO_LIGHTNING_PROGRAM_ID = new PublicKey3(
|
|
1183
|
+
"5sjEbPiqgZrYwR31ahR6Uk9wf5awoX61YGg7jExQSwaj"
|
|
1184
|
+
);
|
|
1185
|
+
var SETTLR_PROGRAM_ID = new PublicKey3(
|
|
1186
|
+
"339A4zncMj8fbM2zvEopYXu6TZqRieJKebDiXCKwquA5"
|
|
1187
|
+
);
|
|
1188
|
+
function findAllowancePda(handle, allowedAddress) {
|
|
1189
|
+
const handleBuffer = Buffer.alloc(16);
|
|
1190
|
+
let h = handle;
|
|
1191
|
+
for (let i = 0; i < 16; i++) {
|
|
1192
|
+
handleBuffer[i] = Number(h & BigInt(255));
|
|
1193
|
+
h = h >> BigInt(8);
|
|
1194
|
+
}
|
|
1195
|
+
return PublicKey3.findProgramAddressSync(
|
|
1196
|
+
[handleBuffer, allowedAddress.toBuffer()],
|
|
1197
|
+
INCO_LIGHTNING_PROGRAM_ID
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
function findPrivateReceiptPda(paymentId) {
|
|
1201
|
+
return PublicKey3.findProgramAddressSync(
|
|
1202
|
+
[Buffer.from("private_receipt"), Buffer.from(paymentId)],
|
|
1203
|
+
SETTLR_PROGRAM_ID
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
async function encryptAmount(amount) {
|
|
1207
|
+
const buffer = Buffer.alloc(16);
|
|
1208
|
+
let a = amount;
|
|
1209
|
+
for (let i = 0; i < 16; i++) {
|
|
1210
|
+
buffer[i] = Number(a & BigInt(255));
|
|
1211
|
+
a = a >> BigInt(8);
|
|
1212
|
+
}
|
|
1213
|
+
return new Uint8Array(buffer);
|
|
1214
|
+
}
|
|
1215
|
+
async function buildPrivateReceiptAccounts(config) {
|
|
1216
|
+
const [privateReceiptPda, privateReceiptBump] = findPrivateReceiptPda(config.paymentId);
|
|
1217
|
+
return {
|
|
1218
|
+
customer: config.customer,
|
|
1219
|
+
merchant: config.merchant,
|
|
1220
|
+
privateReceipt: privateReceiptPda,
|
|
1221
|
+
incoLightningProgram: INCO_LIGHTNING_PROGRAM_ID,
|
|
1222
|
+
systemProgram: SystemProgram.programId,
|
|
1223
|
+
bump: privateReceiptBump
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
async function simulateAndGetHandle(connection, transaction, privateReceiptPda) {
|
|
1227
|
+
try {
|
|
1228
|
+
const simulation = await connection.simulateTransaction(
|
|
1229
|
+
transaction,
|
|
1230
|
+
void 0,
|
|
1231
|
+
[privateReceiptPda]
|
|
1232
|
+
);
|
|
1233
|
+
if (simulation.value.err) {
|
|
1234
|
+
console.error("Simulation failed:", simulation.value.err);
|
|
1235
|
+
return null;
|
|
1236
|
+
}
|
|
1237
|
+
if (simulation.value.accounts?.[0]?.data) {
|
|
1238
|
+
const data = Buffer.from(simulation.value.accounts[0].data[0], "base64");
|
|
1239
|
+
const paymentIdLen = data.readUInt32LE(8);
|
|
1240
|
+
const handleOffset = 8 + 4 + paymentIdLen + 32 + 32;
|
|
1241
|
+
let handle = BigInt(0);
|
|
1242
|
+
for (let i = 15; i >= 0; i--) {
|
|
1243
|
+
handle = handle * BigInt(256) + BigInt(data[handleOffset + i]);
|
|
1244
|
+
}
|
|
1245
|
+
return handle;
|
|
1246
|
+
}
|
|
1247
|
+
return null;
|
|
1248
|
+
} catch (error) {
|
|
1249
|
+
console.error("Simulation error:", error);
|
|
1250
|
+
return null;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
function buildAllowanceRemainingAccounts(handle, customer, merchant) {
|
|
1254
|
+
const [customerAllowancePda] = findAllowancePda(handle, customer);
|
|
1255
|
+
const [merchantAllowancePda] = findAllowancePda(handle, merchant);
|
|
1256
|
+
return [
|
|
1257
|
+
{ pubkey: customerAllowancePda, isSigner: false, isWritable: true },
|
|
1258
|
+
{ pubkey: merchantAllowancePda, isSigner: false, isWritable: true }
|
|
1259
|
+
];
|
|
1260
|
+
}
|
|
1261
|
+
var PrivacyFeatures = {
|
|
1262
|
+
/** Amount is FHE-encrypted, only handle stored on-chain */
|
|
1263
|
+
ENCRYPTED_AMOUNTS: true,
|
|
1264
|
+
/** Selective disclosure - only merchant + customer can decrypt */
|
|
1265
|
+
ACCESS_CONTROL: true,
|
|
1266
|
+
/** CSV export still works (decrypts server-side for authorized merchant) */
|
|
1267
|
+
ACCOUNTING_COMPATIBLE: true,
|
|
1268
|
+
/** Inco covalidators ensure trustless decryption */
|
|
1269
|
+
TRUSTLESS_DECRYPTION: true
|
|
1270
|
+
};
|
|
1271
|
+
var BillingCycles = {
|
|
1272
|
+
/** Weekly (7 days) */
|
|
1273
|
+
WEEKLY: 7 * 24 * 60 * 60,
|
|
1274
|
+
/** Bi-weekly (14 days) */
|
|
1275
|
+
BIWEEKLY: 14 * 24 * 60 * 60,
|
|
1276
|
+
/** Monthly (30 days) */
|
|
1277
|
+
MONTHLY: 30 * 24 * 60 * 60,
|
|
1278
|
+
/** Quarterly (90 days) */
|
|
1279
|
+
QUARTERLY: 90 * 24 * 60 * 60,
|
|
1280
|
+
/** Yearly (365 days) */
|
|
1281
|
+
YEARLY: 365 * 24 * 60 * 60
|
|
1282
|
+
};
|
|
1283
|
+
|
|
1284
|
+
// src/one-click.ts
|
|
1285
|
+
var OneClickClient = class {
|
|
1286
|
+
constructor(baseUrl = "https://settlr.dev") {
|
|
1287
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
1288
|
+
}
|
|
1289
|
+
/**
|
|
1290
|
+
* Customer approves a spending limit for a merchant
|
|
1291
|
+
*/
|
|
1292
|
+
async approve(options) {
|
|
1293
|
+
const response = await fetch(`${this.baseUrl}/api/one-click`, {
|
|
1294
|
+
method: "POST",
|
|
1295
|
+
headers: { "Content-Type": "application/json" },
|
|
1296
|
+
body: JSON.stringify({
|
|
1297
|
+
action: "approve",
|
|
1298
|
+
...options
|
|
1299
|
+
})
|
|
1300
|
+
});
|
|
1301
|
+
const data = await response.json();
|
|
1302
|
+
if (!response.ok) {
|
|
1303
|
+
throw new Error(data.error || "Failed to create approval");
|
|
1304
|
+
}
|
|
1305
|
+
return {
|
|
1306
|
+
success: true,
|
|
1307
|
+
approval: data.approval
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Check if customer has active approval for merchant
|
|
1312
|
+
*/
|
|
1313
|
+
async check(customerWallet, merchantWallet) {
|
|
1314
|
+
const response = await fetch(`${this.baseUrl}/api/one-click`, {
|
|
1315
|
+
method: "POST",
|
|
1316
|
+
headers: { "Content-Type": "application/json" },
|
|
1317
|
+
body: JSON.stringify({
|
|
1318
|
+
action: "check",
|
|
1319
|
+
customerWallet,
|
|
1320
|
+
merchantWallet
|
|
1321
|
+
})
|
|
1322
|
+
});
|
|
1323
|
+
const data = await response.json();
|
|
1324
|
+
return {
|
|
1325
|
+
hasApproval: data.hasApproval || false,
|
|
1326
|
+
remainingLimit: data.remainingLimit,
|
|
1327
|
+
approval: data.approval
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Merchant charges customer using their one-click approval
|
|
1332
|
+
* No customer interaction required if approval exists with sufficient limit
|
|
1333
|
+
*/
|
|
1334
|
+
async charge(options) {
|
|
1335
|
+
const response = await fetch(`${this.baseUrl}/api/one-click`, {
|
|
1336
|
+
method: "POST",
|
|
1337
|
+
headers: { "Content-Type": "application/json" },
|
|
1338
|
+
body: JSON.stringify({
|
|
1339
|
+
action: "charge",
|
|
1340
|
+
...options
|
|
1341
|
+
})
|
|
1342
|
+
});
|
|
1343
|
+
const data = await response.json();
|
|
1344
|
+
if (!response.ok) {
|
|
1345
|
+
return {
|
|
1346
|
+
success: false,
|
|
1347
|
+
error: data.error
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
return {
|
|
1351
|
+
success: true,
|
|
1352
|
+
txSignature: data.txSignature,
|
|
1353
|
+
remainingLimit: data.remainingLimit
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
1357
|
+
* Customer revokes merchant's one-click access
|
|
1358
|
+
*/
|
|
1359
|
+
async revoke(customerWallet, merchantWallet) {
|
|
1360
|
+
const response = await fetch(`${this.baseUrl}/api/one-click`, {
|
|
1361
|
+
method: "POST",
|
|
1362
|
+
headers: { "Content-Type": "application/json" },
|
|
1363
|
+
body: JSON.stringify({
|
|
1364
|
+
action: "revoke",
|
|
1365
|
+
customerWallet,
|
|
1366
|
+
merchantWallet
|
|
1367
|
+
})
|
|
1368
|
+
});
|
|
1369
|
+
return { success: response.ok };
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
function createOneClickClient(baseUrl) {
|
|
1373
|
+
return new OneClickClient(baseUrl);
|
|
1374
|
+
}
|
|
1179
1375
|
export {
|
|
1180
1376
|
BuyButton,
|
|
1181
1377
|
CheckoutWidget,
|
|
1378
|
+
INCO_LIGHTNING_PROGRAM_ID,
|
|
1379
|
+
OneClickClient,
|
|
1182
1380
|
PaymentModal,
|
|
1381
|
+
PrivacyFeatures,
|
|
1183
1382
|
SETTLR_CHECKOUT_URL,
|
|
1383
|
+
SETTLR_PROGRAM_ID,
|
|
1184
1384
|
SUPPORTED_NETWORKS,
|
|
1185
1385
|
SUPPORTED_TOKENS,
|
|
1186
1386
|
Settlr,
|
|
@@ -1189,13 +1389,20 @@ export {
|
|
|
1189
1389
|
USDC_MINT_MAINNET,
|
|
1190
1390
|
USDT_MINT_DEVNET,
|
|
1191
1391
|
USDT_MINT_MAINNET,
|
|
1392
|
+
buildAllowanceRemainingAccounts,
|
|
1393
|
+
buildPrivateReceiptAccounts,
|
|
1394
|
+
createOneClickClient,
|
|
1192
1395
|
createWebhookHandler,
|
|
1396
|
+
encryptAmount,
|
|
1397
|
+
findAllowancePda,
|
|
1398
|
+
findPrivateReceiptPda,
|
|
1193
1399
|
formatUSDC,
|
|
1194
1400
|
getTokenDecimals,
|
|
1195
1401
|
getTokenMint,
|
|
1196
1402
|
parseUSDC,
|
|
1197
1403
|
parseWebhookPayload,
|
|
1198
1404
|
shortenAddress,
|
|
1405
|
+
simulateAndGetHandle,
|
|
1199
1406
|
usePaymentLink,
|
|
1200
1407
|
usePaymentModal,
|
|
1201
1408
|
useSettlr,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@settlr/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Settlr SDK - Accept Solana USDC payments
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Settlr SDK - Accept Solana USDC payments with privacy. Email checkout, gasless transactions, FHE-encrypted receipts. Private on-chain, compliant off-chain.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|