@ic-pay/icpay-sdk 1.4.77 → 1.4.93
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 +35 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +318 -172
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -118,6 +118,122 @@ function u8FromBase64(b64) {
|
|
|
118
118
|
arr[i] = bin.charCodeAt(i);
|
|
119
119
|
return arr;
|
|
120
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Normalize Solana signTransaction result to base64 signed transaction.
|
|
123
|
+
* Wallets may return: signedTransaction, transaction, signedMessage (string base64/base58),
|
|
124
|
+
* serializedTransaction (Uint8Array), or the raw value. Phantom may return base58.
|
|
125
|
+
*/
|
|
126
|
+
function normalizeSolanaSignedTransaction(r) {
|
|
127
|
+
if (r == null)
|
|
128
|
+
return null;
|
|
129
|
+
const toB64 = (val) => {
|
|
130
|
+
if (val == null)
|
|
131
|
+
return null;
|
|
132
|
+
if (typeof val === 'string') {
|
|
133
|
+
const looksBase64 = /^[A-Za-z0-9+/=]+$/.test(val) && val.length >= 80 && val.length % 4 === 0;
|
|
134
|
+
if (looksBase64)
|
|
135
|
+
return val;
|
|
136
|
+
try {
|
|
137
|
+
const decoded = base58Decode(val);
|
|
138
|
+
if (decoded.length > 64)
|
|
139
|
+
return b64FromBytes(decoded);
|
|
140
|
+
}
|
|
141
|
+
catch { }
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
if (val.byteLength != null || ArrayBuffer.isView(val)) {
|
|
145
|
+
const b = val instanceof Uint8Array ? val : new Uint8Array(val);
|
|
146
|
+
if (b.length > 64)
|
|
147
|
+
return b64FromBytes(b);
|
|
148
|
+
}
|
|
149
|
+
if (typeof val === 'object' && typeof val.serialize === 'function') {
|
|
150
|
+
try {
|
|
151
|
+
const out = val.serialize({ requireAllSignatures: false, verifySignatures: false });
|
|
152
|
+
const b = out instanceof Uint8Array ? out : new Uint8Array(out);
|
|
153
|
+
if (b.length > 64)
|
|
154
|
+
return b64FromBytes(b);
|
|
155
|
+
}
|
|
156
|
+
catch { }
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
};
|
|
160
|
+
const direct = toB64(r);
|
|
161
|
+
if (direct)
|
|
162
|
+
return direct;
|
|
163
|
+
if (typeof r === 'object') {
|
|
164
|
+
for (const key of ['signedTransaction', 'transaction', 'signedMessage', 'serializedTransaction', 'encodedTransaction', 'message']) {
|
|
165
|
+
const v = r[key];
|
|
166
|
+
const b64 = toB64(v);
|
|
167
|
+
if (b64)
|
|
168
|
+
return b64;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
/** Normalize Solana signMessage result to base64 64-byte signature. Phantom returns { signature, rawSignature }; some wallets return { signature: Uint8Array } or string. */
|
|
174
|
+
function normalizeSolanaMessageSignature(r) {
|
|
175
|
+
if (!r)
|
|
176
|
+
return null;
|
|
177
|
+
// Direct string (base58 or base64)
|
|
178
|
+
if (typeof r === 'string') {
|
|
179
|
+
if (r.length === 88 && /^[1-9A-HJ-NP-Za-km-z]+$/.test(r)) {
|
|
180
|
+
try {
|
|
181
|
+
const b = base58Decode(r);
|
|
182
|
+
return b.length === 64 ? b64FromBytes(b) : null;
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const b = u8FromBase64(r);
|
|
190
|
+
return b.length === 64 ? b64FromBytes(b) : null;
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Byte-like (Uint8Array / ArrayBufferView)
|
|
197
|
+
if (r.byteLength != null || ArrayBuffer.isView(r)) {
|
|
198
|
+
const b = r instanceof Uint8Array ? r : new Uint8Array(r);
|
|
199
|
+
return b.length === 64 ? b64FromBytes(b) : null;
|
|
200
|
+
}
|
|
201
|
+
// Object: signature (string or Uint8Array) or rawSignature (base64 string)
|
|
202
|
+
if (typeof r === 'object') {
|
|
203
|
+
const sig = r.signature;
|
|
204
|
+
if (sig != null) {
|
|
205
|
+
if (typeof sig === 'string') {
|
|
206
|
+
try {
|
|
207
|
+
const b = base58Decode(sig);
|
|
208
|
+
return b.length === 64 ? b64FromBytes(b) : null;
|
|
209
|
+
}
|
|
210
|
+
catch { }
|
|
211
|
+
try {
|
|
212
|
+
const b = u8FromBase64(sig);
|
|
213
|
+
return b.length === 64 ? b64FromBytes(b) : null;
|
|
214
|
+
}
|
|
215
|
+
catch { }
|
|
216
|
+
}
|
|
217
|
+
if (sig.byteLength != null || ArrayBuffer.isView(sig)) {
|
|
218
|
+
const b = sig instanceof Uint8Array ? sig : new Uint8Array(sig);
|
|
219
|
+
return b.length === 64 ? b64FromBytes(b) : null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const raw = r.rawSignature;
|
|
223
|
+
if (typeof raw === 'string') {
|
|
224
|
+
try {
|
|
225
|
+
const b = u8FromBase64(raw);
|
|
226
|
+
return b.length === 64 ? b64FromBytes(b) : null;
|
|
227
|
+
}
|
|
228
|
+
catch { }
|
|
229
|
+
}
|
|
230
|
+
if (Array.isArray(r.data)) {
|
|
231
|
+
const b = Uint8Array.from(r.data);
|
|
232
|
+
return b.length === 64 ? b64FromBytes(b) : null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
121
237
|
// Normalize metadata so internal icpay-managed fields are nested under metadata.icpay.
|
|
122
238
|
// If icpay exists, merge; otherwise create. Move known internal keys and icpay_* keys under icpay.
|
|
123
239
|
function normalizeSdkMetadata(base) {
|
|
@@ -800,6 +916,7 @@ class Icpay {
|
|
|
800
916
|
const prebuiltBase64 = params.request?.__transactionBase64;
|
|
801
917
|
if (typeof prebuiltBase64 === 'string' && prebuiltBase64.length > 0) {
|
|
802
918
|
let signature = null;
|
|
919
|
+
let relay;
|
|
803
920
|
try {
|
|
804
921
|
if (sol?.request) {
|
|
805
922
|
// Treat as Phantom only if the selected provider itself reports isPhantom,
|
|
@@ -835,76 +952,62 @@ class Icpay {
|
|
|
835
952
|
}
|
|
836
953
|
catch { }
|
|
837
954
|
}
|
|
838
|
-
|
|
839
|
-
if (
|
|
840
|
-
const
|
|
841
|
-
if (
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
if (
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
if (typeof
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
const
|
|
864
|
-
if (
|
|
865
|
-
|
|
955
|
+
signedTxB64 = normalizeSolanaSignedTransaction(r) ?? signedTxB64;
|
|
956
|
+
if (!signedTxB64 && !signerSigBase58) {
|
|
957
|
+
const candidate = (r?.signedTransaction || r?.transaction || r?.signedMessage || r);
|
|
958
|
+
if (typeof candidate === 'string') {
|
|
959
|
+
const looksBase64 = /^[A-Za-z0-9+/=]+$/.test(candidate) && candidate.length % 4 === 0;
|
|
960
|
+
if (looksBase64)
|
|
961
|
+
signedTxB64 = candidate;
|
|
962
|
+
else
|
|
963
|
+
signerSigBase58 = candidate;
|
|
964
|
+
}
|
|
965
|
+
else if (candidate && (candidate.byteLength != null || ArrayBuffer.isView(candidate))) {
|
|
966
|
+
const b = candidate instanceof Uint8Array ? candidate : new Uint8Array(candidate);
|
|
967
|
+
if (b.length > 64)
|
|
968
|
+
signedTxB64 = b64FromBytes(b);
|
|
969
|
+
else if (b.length === 64)
|
|
970
|
+
signerSigBase58 = base58Encode(b);
|
|
971
|
+
}
|
|
972
|
+
else if (r && typeof r === 'object') {
|
|
973
|
+
const obj = r;
|
|
974
|
+
if (typeof obj.signedTransaction === 'string')
|
|
975
|
+
signedTxB64 = obj.signedTransaction;
|
|
976
|
+
if (!signedTxB64 && typeof obj.signature === 'string')
|
|
977
|
+
signerSigBase58 = obj.signature;
|
|
978
|
+
if (!signedTxB64 && obj && typeof obj.serialize === 'function') {
|
|
979
|
+
try {
|
|
980
|
+
const out = obj.serialize({ requireAllSignatures: false, verifySignatures: false });
|
|
981
|
+
if (out && (out.byteLength != null || ArrayBuffer.isView(out))) {
|
|
982
|
+
const b = out instanceof Uint8Array ? out : new Uint8Array(out);
|
|
983
|
+
if (b.length > 64)
|
|
984
|
+
signedTxB64 = b64FromBytes(b);
|
|
985
|
+
}
|
|
866
986
|
}
|
|
987
|
+
catch { }
|
|
867
988
|
}
|
|
868
|
-
catch { }
|
|
869
989
|
}
|
|
870
990
|
}
|
|
871
|
-
// Direct provider API fallback (Phantom: signTransaction(Transaction))
|
|
872
991
|
if (!signedTxB64 && typeof sol.signTransaction === 'function') {
|
|
873
992
|
try {
|
|
874
993
|
const txBytes = u8FromBase64(prebuiltBase64);
|
|
875
994
|
const stx = await sol.signTransaction(txBytes);
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
try {
|
|
886
|
-
const out = stx.serialize({ requireAllSignatures: false, verifySignatures: false });
|
|
887
|
-
const b = out instanceof Uint8Array ? out : new Uint8Array(out);
|
|
888
|
-
if (b.length > 64)
|
|
889
|
-
signedTxB64 = b64FromBytes(b);
|
|
890
|
-
}
|
|
891
|
-
catch { }
|
|
892
|
-
}
|
|
893
|
-
else if (stx && (stx.byteLength != null || ArrayBuffer.isView(stx))) {
|
|
894
|
-
const b = stx instanceof Uint8Array ? stx : new Uint8Array(stx);
|
|
895
|
-
if (b.length > 64)
|
|
896
|
-
signedTxB64 = b64FromBytes(b);
|
|
897
|
-
else if (b.length === 64)
|
|
898
|
-
signerSigBase58 = base58Encode(b);
|
|
899
|
-
}
|
|
995
|
+
const parsed = normalizeSolanaSignedTransaction(stx);
|
|
996
|
+
if (parsed)
|
|
997
|
+
signedTxB64 = parsed;
|
|
998
|
+
else if (stx && typeof stx === 'string' && stx.length === 88)
|
|
999
|
+
signerSigBase58 = stx;
|
|
1000
|
+
else if (stx && (stx.byteLength != null || ArrayBuffer.isView(stx))) {
|
|
1001
|
+
const b = stx instanceof Uint8Array ? stx : new Uint8Array(stx);
|
|
1002
|
+
if (b.length === 64)
|
|
1003
|
+
signerSigBase58 = base58Encode(b);
|
|
900
1004
|
}
|
|
901
1005
|
}
|
|
902
1006
|
catch { }
|
|
903
1007
|
}
|
|
904
1008
|
if (!signedTxB64 && !signerSigBase58)
|
|
905
1009
|
throw new Error('Wallet did not return a signed transaction');
|
|
906
|
-
// Relay via API
|
|
907
|
-
let relay;
|
|
1010
|
+
// Relay via API. Standard flow prebuilt tx has payer as fee payer (no facilitatorPaysFee).
|
|
908
1011
|
if (signedTxB64) {
|
|
909
1012
|
relay = await this.publicApiClient.post('/sdk/public/payments/solana/relay', {
|
|
910
1013
|
signedTransactionBase64: signedTxB64,
|
|
@@ -976,6 +1079,25 @@ class Icpay {
|
|
|
976
1079
|
this.emitMethodSuccess('notifyLedgerTransaction', { paymentIntentId: params.paymentIntentId });
|
|
977
1080
|
}
|
|
978
1081
|
catch { }
|
|
1082
|
+
// If relay already returned completed payload (paymentIntent + payment), skip notify and polling
|
|
1083
|
+
const relayPayload = typeof relay?.paymentIntent !== 'undefined' || typeof relay?.payment !== 'undefined' ? relay : undefined;
|
|
1084
|
+
const relayStatus = relayPayload && (typeof relayPayload.status === 'string' ? relayPayload.status : relayPayload?.paymentIntent?.status || relayPayload?.payment?.status || '');
|
|
1085
|
+
const relayTerminal = typeof relayStatus === 'string' && ['completed', 'succeeded'].includes(String(relayStatus).toLowerCase());
|
|
1086
|
+
if (relayPayload && relayTerminal) {
|
|
1087
|
+
const norm = String(relayStatus).toLowerCase();
|
|
1088
|
+
const out = {
|
|
1089
|
+
transactionId: 0,
|
|
1090
|
+
status: norm === 'succeeded' ? 'completed' : norm,
|
|
1091
|
+
amount: params.amount.toString(),
|
|
1092
|
+
recipientCanister: params.ledgerCanisterId,
|
|
1093
|
+
timestamp: new Date(),
|
|
1094
|
+
description: 'Fund transfer',
|
|
1095
|
+
metadata: { ...(params.metadata || {}), icpay_solana_tx_sig: signature },
|
|
1096
|
+
payment: relayPayload,
|
|
1097
|
+
};
|
|
1098
|
+
this.emit('icpay-sdk-transaction-completed', out);
|
|
1099
|
+
return out;
|
|
1100
|
+
}
|
|
979
1101
|
try {
|
|
980
1102
|
await this.performNotifyPaymentIntent({ paymentIntentId: params.paymentIntentId, transactionId: signature, maxAttempts: 1 });
|
|
981
1103
|
}
|
|
@@ -1405,6 +1527,7 @@ class Icpay {
|
|
|
1405
1527
|
recipientAddress,
|
|
1406
1528
|
recipientAddresses: request?.recipientAddresses || undefined,
|
|
1407
1529
|
externalCostAmount: request?.externalCostAmount ?? request?.metadata?.externalCostAmount ?? undefined,
|
|
1530
|
+
fiat_currency: request?.fiat_currency,
|
|
1408
1531
|
});
|
|
1409
1532
|
}
|
|
1410
1533
|
else {
|
|
@@ -1416,6 +1539,7 @@ class Icpay {
|
|
|
1416
1539
|
metadata: normalizeSdkMetadata(request.metadata || {}),
|
|
1417
1540
|
widgetParams: request.widgetParams || undefined,
|
|
1418
1541
|
recipientAddresses: request?.recipientAddresses || undefined,
|
|
1542
|
+
fiat_currency: request?.fiat_currency,
|
|
1419
1543
|
});
|
|
1420
1544
|
}
|
|
1421
1545
|
else {
|
|
@@ -1436,6 +1560,7 @@ class Icpay {
|
|
|
1436
1560
|
recipientAddress,
|
|
1437
1561
|
recipientAddresses: request?.recipientAddresses || undefined,
|
|
1438
1562
|
externalCostAmount: request?.externalCostAmount ?? request?.metadata?.externalCostAmount ?? undefined,
|
|
1563
|
+
fiat_currency: request?.fiat_currency,
|
|
1439
1564
|
});
|
|
1440
1565
|
}
|
|
1441
1566
|
}
|
|
@@ -1851,6 +1976,8 @@ class Icpay {
|
|
|
1851
1976
|
search.set('amountUsd', String(params.amountUsd));
|
|
1852
1977
|
if (typeof params.amount === 'string' && params.amount)
|
|
1853
1978
|
search.set('amount', params.amount);
|
|
1979
|
+
if (typeof params.fiatCurrency === 'string' && params.fiatCurrency.trim())
|
|
1980
|
+
search.set('fiatCurrency', params.fiatCurrency.trim());
|
|
1854
1981
|
if (Array.isArray(params.chainShortcodes) && params.chainShortcodes.length > 0)
|
|
1855
1982
|
search.set('chainShortcodes', params.chainShortcodes.join(','));
|
|
1856
1983
|
if (Array.isArray(params.tokenShortcodes) && params.tokenShortcodes.length > 0)
|
|
@@ -2092,6 +2219,7 @@ class Icpay {
|
|
|
2092
2219
|
chainId: tokenShortcode ? undefined : request.chainId,
|
|
2093
2220
|
recipientAddress: request?.recipientAddress || '0x0000000000000000000000000000000000000000',
|
|
2094
2221
|
recipientAddresses: request?.recipientAddresses,
|
|
2222
|
+
fiat_currency: request?.fiat_currency ?? request?.fiatCurrency ?? this.config?.fiat_currency,
|
|
2095
2223
|
};
|
|
2096
2224
|
const res = await this.createPayment(createTransactionRequest);
|
|
2097
2225
|
this.emitMethodSuccess('createPaymentUsd', res);
|
|
@@ -2136,11 +2264,15 @@ class Icpay {
|
|
|
2136
2264
|
chainId: tokenShortcode ? undefined : request.chainId,
|
|
2137
2265
|
x402: true,
|
|
2138
2266
|
recipientAddress: request?.recipientAddress || '0x0000000000000000000000000000000000000000',
|
|
2267
|
+
fiat_currency: request?.fiat_currency ?? request?.fiatCurrency ?? this.config?.fiat_currency,
|
|
2139
2268
|
};
|
|
2140
|
-
|
|
2269
|
+
if (body.fiat_currency === undefined || body.fiat_currency === '') {
|
|
2270
|
+
delete body.fiat_currency;
|
|
2271
|
+
}
|
|
2272
|
+
// Include Solana payerPublicKey so server can build unsigned tx (standard x402 flow)
|
|
2141
2273
|
try {
|
|
2142
2274
|
const w = globalThis?.window || globalThis;
|
|
2143
|
-
const sol = this.config?.solanaProvider || w?.solana || w?.phantom?.solana;
|
|
2275
|
+
const sol = this.config?.solanaProvider || this.config?.connectedWallet?.solana || this.config?.connectedWallet || w?.solana || w?.phantom?.solana;
|
|
2144
2276
|
const pk = sol?.publicKey?.toBase58?.() || sol?.publicKey || null;
|
|
2145
2277
|
if (pk && typeof pk === 'string') {
|
|
2146
2278
|
body.payerPublicKey = pk;
|
|
@@ -2355,10 +2487,12 @@ class Icpay {
|
|
|
2355
2487
|
});
|
|
2356
2488
|
}
|
|
2357
2489
|
if (isSol) {
|
|
2358
|
-
// Solana x402:
|
|
2490
|
+
// Solana x402: follow standard flow (https://solana.com/developers/guides/getstarted/intro-to-x402)
|
|
2491
|
+
// — client signs transaction with signTransaction, then relay. No signMessage.
|
|
2492
|
+
const solTxBase64 = requirement?.extra?.transactionBase64;
|
|
2493
|
+
const solMsgB58 = requirement?.extra?.messageBase58;
|
|
2359
2494
|
const signableMsgB64 = requirement?.extra?.signableMessageBase64;
|
|
2360
2495
|
const signableFields = requirement?.extra?.signableFields || {};
|
|
2361
|
-
const wSolCtx = globalThis?.window || globalThis;
|
|
2362
2496
|
const sol = providerForHeader;
|
|
2363
2497
|
if (!sol)
|
|
2364
2498
|
throw new errors_1.IcpayError({ code: errors_1.ICPAY_ERROR_CODES.WALLET_PROVIDER_NOT_AVAILABLE, message: 'Solana provider not available (window.solana)' });
|
|
@@ -2377,7 +2511,120 @@ class Icpay {
|
|
|
2377
2511
|
}
|
|
2378
2512
|
if (!fromBase58)
|
|
2379
2513
|
throw new errors_1.IcpayError({ code: errors_1.ICPAY_ERROR_CODES.WALLET_NOT_CONNECTED, message: 'Solana wallet not connected' });
|
|
2380
|
-
//
|
|
2514
|
+
// Standard x402: when server provided unsigned tx, sign it and relay (no signMessage)
|
|
2515
|
+
if (solTxBase64 && solTxBase64.length > 0) {
|
|
2516
|
+
if (typeof sol?.connect === 'function') {
|
|
2517
|
+
try {
|
|
2518
|
+
await sol.connect({ onlyIfTrusted: false });
|
|
2519
|
+
}
|
|
2520
|
+
catch { }
|
|
2521
|
+
}
|
|
2522
|
+
const __txB64 = String(solTxBase64);
|
|
2523
|
+
const inlineMsgB58 = solMsgB58 && solMsgB58.length > 0 ? String(solMsgB58) : base58Encode(u8FromBase64(__txB64));
|
|
2524
|
+
let signedTxB64 = null;
|
|
2525
|
+
let r = null;
|
|
2526
|
+
if (sol?.request) {
|
|
2527
|
+
try {
|
|
2528
|
+
try {
|
|
2529
|
+
r = await sol.request({ method: 'signTransaction', params: { message: inlineMsgB58 } });
|
|
2530
|
+
}
|
|
2531
|
+
catch { }
|
|
2532
|
+
if (!r)
|
|
2533
|
+
try {
|
|
2534
|
+
r = await sol.request({ method: 'signTransaction', params: inlineMsgB58 });
|
|
2535
|
+
}
|
|
2536
|
+
catch { }
|
|
2537
|
+
if (!r)
|
|
2538
|
+
try {
|
|
2539
|
+
r = await sol.request({ method: 'solana:signTransaction', params: { transaction: __txB64 } });
|
|
2540
|
+
}
|
|
2541
|
+
catch { }
|
|
2542
|
+
if (!r)
|
|
2543
|
+
try {
|
|
2544
|
+
r = await sol.request({ method: 'signTransaction', params: { transaction: __txB64 } });
|
|
2545
|
+
}
|
|
2546
|
+
catch { }
|
|
2547
|
+
}
|
|
2548
|
+
catch { }
|
|
2549
|
+
}
|
|
2550
|
+
signedTxB64 = normalizeSolanaSignedTransaction(r) ?? signedTxB64;
|
|
2551
|
+
if (!signedTxB64 && r) {
|
|
2552
|
+
const candidate = (r?.signedTransaction || r?.transaction || r?.signedMessage || r);
|
|
2553
|
+
if (typeof candidate === 'string') {
|
|
2554
|
+
const looksBase64 = /^[A-Za-z0-9+/=]+$/.test(candidate) && candidate.length % 4 === 0;
|
|
2555
|
+
if (looksBase64)
|
|
2556
|
+
signedTxB64 = candidate;
|
|
2557
|
+
}
|
|
2558
|
+
else if (candidate && (candidate.byteLength != null || ArrayBuffer.isView(candidate))) {
|
|
2559
|
+
const b = candidate instanceof Uint8Array ? candidate : new Uint8Array(candidate);
|
|
2560
|
+
if (b.length > 64)
|
|
2561
|
+
signedTxB64 = b64FromBytes(b);
|
|
2562
|
+
}
|
|
2563
|
+
else if (typeof r === 'object') {
|
|
2564
|
+
const obj = r;
|
|
2565
|
+
if (typeof obj.signedTransaction === 'string')
|
|
2566
|
+
signedTxB64 = obj.signedTransaction;
|
|
2567
|
+
else if (typeof obj.transaction === 'string')
|
|
2568
|
+
signedTxB64 = obj.transaction;
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
if (!signedTxB64 && typeof sol.signTransaction === 'function') {
|
|
2572
|
+
try {
|
|
2573
|
+
const txBytes = u8FromBase64(__txB64);
|
|
2574
|
+
const stx = await sol.signTransaction(txBytes);
|
|
2575
|
+
signedTxB64 = normalizeSolanaSignedTransaction(stx) ?? signedTxB64;
|
|
2576
|
+
}
|
|
2577
|
+
catch { }
|
|
2578
|
+
}
|
|
2579
|
+
if (signedTxB64) {
|
|
2580
|
+
const relay = await this.publicApiClient.post('/sdk/public/payments/solana/relay', {
|
|
2581
|
+
signedTransactionBase64: signedTxB64,
|
|
2582
|
+
paymentIntentId,
|
|
2583
|
+
facilitatorPaysFee: true,
|
|
2584
|
+
});
|
|
2585
|
+
const sig = (relay && relay.signature) || null;
|
|
2586
|
+
try {
|
|
2587
|
+
this.emitMethodSuccess('notifyLedgerTransaction', { paymentIntentId });
|
|
2588
|
+
}
|
|
2589
|
+
catch { }
|
|
2590
|
+
if (sig) {
|
|
2591
|
+
try {
|
|
2592
|
+
await this.performNotifyPaymentIntent({ paymentIntentId, transactionId: sig, maxAttempts: 1 });
|
|
2593
|
+
}
|
|
2594
|
+
catch { }
|
|
2595
|
+
}
|
|
2596
|
+
const relayStatus = relay?.status || relay?.paymentIntent?.status || relay?.payment?.status || '';
|
|
2597
|
+
const terminal = typeof relayStatus === 'string' && ['completed', 'succeeded'].includes(String(relayStatus).toLowerCase());
|
|
2598
|
+
if (terminal) {
|
|
2599
|
+
const out = {
|
|
2600
|
+
transactionId: 0,
|
|
2601
|
+
status: String(relayStatus).toLowerCase() === 'succeeded' ? 'completed' : relayStatus,
|
|
2602
|
+
amount: requirement?.maxAmountRequired?.toString?.() || '',
|
|
2603
|
+
recipientCanister: ledgerCanisterId,
|
|
2604
|
+
timestamp: new Date(),
|
|
2605
|
+
metadata: { ...(request.metadata || {}), icpay_x402: true, icpay_solana_tx_sig: sig },
|
|
2606
|
+
payment: relay,
|
|
2607
|
+
};
|
|
2608
|
+
this.emit('icpay-sdk-transaction-completed', out);
|
|
2609
|
+
this.emitMethodSuccess('createPaymentX402Usd', out);
|
|
2610
|
+
return out;
|
|
2611
|
+
}
|
|
2612
|
+
const waited = await this.awaitIntentTerminal({
|
|
2613
|
+
paymentIntentId,
|
|
2614
|
+
transactionId: sig,
|
|
2615
|
+
ledgerCanisterId: ledgerCanisterId,
|
|
2616
|
+
amount: requirement?.maxAmountRequired?.toString?.() || '',
|
|
2617
|
+
metadata: { ...(request.metadata || {}), icpay_x402: true, icpay_solana_tx_sig: sig },
|
|
2618
|
+
});
|
|
2619
|
+
this.emitMethodSuccess('createPaymentX402Usd', waited);
|
|
2620
|
+
return waited;
|
|
2621
|
+
}
|
|
2622
|
+
throw new errors_1.IcpayError({
|
|
2623
|
+
code: errors_1.ICPAY_ERROR_CODES.TRANSACTION_FAILED,
|
|
2624
|
+
message: 'Transaction was not signed. Please approve the transaction in your wallet.',
|
|
2625
|
+
});
|
|
2626
|
+
}
|
|
2627
|
+
// Fallback: message-sign flow only when no transactionBase64 (e.g. 402 built without payerPublicKey)
|
|
2381
2628
|
if (signableMsgB64) {
|
|
2382
2629
|
// Sign the provided message and settle via header (services will submit)
|
|
2383
2630
|
// Ensure explicit connect prompt before signing
|
|
@@ -2391,7 +2638,7 @@ class Icpay {
|
|
|
2391
2638
|
const msgBytes = u8FromBase64(signableMsgB64);
|
|
2392
2639
|
const msgB58ForReq = base58Encode(msgBytes);
|
|
2393
2640
|
// Attempts in order (strict):
|
|
2394
|
-
// 1) Wallet Standard: request colon form with Uint8Array
|
|
2641
|
+
// 1) Wallet Standard: request colon form with Uint8Array (Phantom returns { signature, rawSignature } or { signature: Uint8Array })
|
|
2395
2642
|
if (!sigB64 && sol?.request) {
|
|
2396
2643
|
try {
|
|
2397
2644
|
try {
|
|
@@ -2399,34 +2646,7 @@ class Icpay {
|
|
|
2399
2646
|
}
|
|
2400
2647
|
catch { }
|
|
2401
2648
|
const r0 = await sol.request({ method: 'solana:signMessage', params: { message: msgBytes } });
|
|
2402
|
-
|
|
2403
|
-
try {
|
|
2404
|
-
const b = base58Decode(r0);
|
|
2405
|
-
sigB64 = b64FromBytes(b);
|
|
2406
|
-
}
|
|
2407
|
-
catch {
|
|
2408
|
-
sigB64 = r0;
|
|
2409
|
-
}
|
|
2410
|
-
}
|
|
2411
|
-
else if (r0 && typeof r0.signature === 'string') {
|
|
2412
|
-
try {
|
|
2413
|
-
const b = base58Decode(r0.signature);
|
|
2414
|
-
sigB64 = b64FromBytes(b);
|
|
2415
|
-
}
|
|
2416
|
-
catch {
|
|
2417
|
-
sigB64 = r0.signature;
|
|
2418
|
-
}
|
|
2419
|
-
}
|
|
2420
|
-
else if (r0 && (r0.byteLength != null || ArrayBuffer.isView(r0))) {
|
|
2421
|
-
const b = r0 instanceof Uint8Array ? r0 : new Uint8Array(r0);
|
|
2422
|
-
if (b && b.length === 64)
|
|
2423
|
-
sigB64 = b64FromBytes(b);
|
|
2424
|
-
}
|
|
2425
|
-
else if (r0 && typeof r0 === 'object' && Array.isArray(r0.data)) {
|
|
2426
|
-
const b = Uint8Array.from(r0.data);
|
|
2427
|
-
if (b && b.length === 64)
|
|
2428
|
-
sigB64 = b64FromBytes(b);
|
|
2429
|
-
}
|
|
2649
|
+
sigB64 = normalizeSolanaMessageSignature(r0) ?? sigB64;
|
|
2430
2650
|
}
|
|
2431
2651
|
catch (e0) {
|
|
2432
2652
|
try {
|
|
@@ -2443,30 +2663,7 @@ class Icpay {
|
|
|
2443
2663
|
}
|
|
2444
2664
|
catch { }
|
|
2445
2665
|
const r2 = await sol.signMessage(msgBytes);
|
|
2446
|
-
|
|
2447
|
-
const b = r2 instanceof Uint8Array ? r2 : new Uint8Array(r2);
|
|
2448
|
-
if (b && b.length === 64)
|
|
2449
|
-
sigB64 = b64FromBytes(b);
|
|
2450
|
-
}
|
|
2451
|
-
else if (typeof r2 === 'string') {
|
|
2452
|
-
try {
|
|
2453
|
-
const b = base58Decode(r2);
|
|
2454
|
-
sigB64 = b64FromBytes(b);
|
|
2455
|
-
}
|
|
2456
|
-
catch {
|
|
2457
|
-
sigB64 = r2;
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
else if (r2 && typeof r2 === 'object' && typeof r2.signature === 'string') {
|
|
2461
|
-
const s = r2.signature;
|
|
2462
|
-
try {
|
|
2463
|
-
const b = base58Decode(s);
|
|
2464
|
-
sigB64 = b64FromBytes(b);
|
|
2465
|
-
}
|
|
2466
|
-
catch {
|
|
2467
|
-
sigB64 = s;
|
|
2468
|
-
}
|
|
2469
|
-
}
|
|
2666
|
+
sigB64 = normalizeSolanaMessageSignature(r2) ?? sigB64;
|
|
2470
2667
|
}
|
|
2471
2668
|
catch (e2) {
|
|
2472
2669
|
try {
|
|
@@ -2483,34 +2680,7 @@ class Icpay {
|
|
|
2483
2680
|
}
|
|
2484
2681
|
catch { }
|
|
2485
2682
|
const r3 = await sol.request({ method: 'signMessage', params: { message: msgBytes } });
|
|
2486
|
-
|
|
2487
|
-
try {
|
|
2488
|
-
const b = base58Decode(r3);
|
|
2489
|
-
sigB64 = b64FromBytes(b);
|
|
2490
|
-
}
|
|
2491
|
-
catch {
|
|
2492
|
-
sigB64 = r3;
|
|
2493
|
-
}
|
|
2494
|
-
}
|
|
2495
|
-
else if (r3 && typeof r3.signature === 'string') {
|
|
2496
|
-
try {
|
|
2497
|
-
const b = base58Decode(r3.signature);
|
|
2498
|
-
sigB64 = b64FromBytes(b);
|
|
2499
|
-
}
|
|
2500
|
-
catch {
|
|
2501
|
-
sigB64 = r3.signature;
|
|
2502
|
-
}
|
|
2503
|
-
}
|
|
2504
|
-
else if (r3 && (r3.byteLength != null || ArrayBuffer.isView(r3))) {
|
|
2505
|
-
const b = r3 instanceof Uint8Array ? r3 : new Uint8Array(r3);
|
|
2506
|
-
if (b && b.length === 64)
|
|
2507
|
-
sigB64 = b64FromBytes(b);
|
|
2508
|
-
}
|
|
2509
|
-
else if (r3 && typeof r3 === 'object' && Array.isArray(r3.data)) {
|
|
2510
|
-
const b = Uint8Array.from(r3.data);
|
|
2511
|
-
if (b && b.length === 64)
|
|
2512
|
-
sigB64 = b64FromBytes(b);
|
|
2513
|
-
}
|
|
2683
|
+
sigB64 = normalizeSolanaMessageSignature(r3) ?? sigB64;
|
|
2514
2684
|
}
|
|
2515
2685
|
catch (e3) {
|
|
2516
2686
|
try {
|
|
@@ -2527,34 +2697,7 @@ class Icpay {
|
|
|
2527
2697
|
}
|
|
2528
2698
|
catch { }
|
|
2529
2699
|
const r4 = await sol.request({ method: 'signMessage', params: { message: msgB58ForReq } });
|
|
2530
|
-
|
|
2531
|
-
try {
|
|
2532
|
-
const b = base58Decode(r4);
|
|
2533
|
-
sigB64 = b64FromBytes(b);
|
|
2534
|
-
}
|
|
2535
|
-
catch {
|
|
2536
|
-
sigB64 = r4;
|
|
2537
|
-
}
|
|
2538
|
-
}
|
|
2539
|
-
else if (r4 && typeof r4.signature === 'string') {
|
|
2540
|
-
try {
|
|
2541
|
-
const b = base58Decode(r4.signature);
|
|
2542
|
-
sigB64 = b64FromBytes(b);
|
|
2543
|
-
}
|
|
2544
|
-
catch {
|
|
2545
|
-
sigB64 = r4.signature;
|
|
2546
|
-
}
|
|
2547
|
-
}
|
|
2548
|
-
else if (r4 && (r4.byteLength != null || ArrayBuffer.isView(r4))) {
|
|
2549
|
-
const b = r4 instanceof Uint8Array ? r4 : new Uint8Array(r4);
|
|
2550
|
-
if (b && b.length === 64)
|
|
2551
|
-
sigB64 = b64FromBytes(b);
|
|
2552
|
-
}
|
|
2553
|
-
else if (r4 && typeof r4 === 'object' && Array.isArray(r4.data)) {
|
|
2554
|
-
const b = Uint8Array.from(r4.data);
|
|
2555
|
-
if (b && b.length === 64)
|
|
2556
|
-
sigB64 = b64FromBytes(b);
|
|
2557
|
-
}
|
|
2700
|
+
sigB64 = normalizeSolanaMessageSignature(r4) ?? sigB64;
|
|
2558
2701
|
}
|
|
2559
2702
|
catch (e4) {
|
|
2560
2703
|
try {
|
|
@@ -2646,10 +2789,13 @@ class Icpay {
|
|
|
2646
2789
|
return waitedSolHdr;
|
|
2647
2790
|
}
|
|
2648
2791
|
else {
|
|
2649
|
-
// Fallback: if API provided an unsigned transaction, try transaction-signing path
|
|
2792
|
+
// Fallback: if API provided an unsigned transaction, try transaction-signing path (signTransaction like normal Solana flow)
|
|
2650
2793
|
const fallbackTx = requirement?.extra?.transactionBase64;
|
|
2651
2794
|
if (!fallbackTx) {
|
|
2652
|
-
throw new errors_1.IcpayError({
|
|
2795
|
+
throw new errors_1.IcpayError({
|
|
2796
|
+
code: errors_1.ICPAY_ERROR_CODES.TRANSACTION_FAILED,
|
|
2797
|
+
message: 'Wallet did not sign message. Connect your Solana wallet before starting the payment and try again.',
|
|
2798
|
+
});
|
|
2653
2799
|
}
|
|
2654
2800
|
// Inject for transaction-signing fallback below
|
|
2655
2801
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|