@ic-pay/icpay-sdk 1.4.84 → 1.4.99
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 +59 -18
- package/dist/http.d.ts +9 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +9 -0
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +207 -99
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +68 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +11 -8
package/dist/index.js
CHANGED
|
@@ -319,12 +319,15 @@ class Icpay {
|
|
|
319
319
|
async notifyPayment(params) {
|
|
320
320
|
this.emitMethodStart('notifyPayment', { paymentIntentId: params.paymentIntentId });
|
|
321
321
|
try {
|
|
322
|
-
const
|
|
322
|
+
const body = {
|
|
323
323
|
paymentIntentId: params.paymentIntentId,
|
|
324
324
|
canisterTxId: params.canisterTxId,
|
|
325
325
|
transactionId: params.transactionId,
|
|
326
326
|
orderId: params.orderId,
|
|
327
|
-
}
|
|
327
|
+
};
|
|
328
|
+
if (params.icpayPaymentLink && typeof params.icpayPaymentLink === 'object')
|
|
329
|
+
body.icpayPaymentLink = params.icpayPaymentLink;
|
|
330
|
+
const resp = await this.publicApiClient.post('/sdk/public/payments/notify', body);
|
|
328
331
|
this.emitMethodSuccess('notifyPayment', { status: resp?.status, paymentIntentId: resp?.paymentIntentId });
|
|
329
332
|
return resp;
|
|
330
333
|
}
|
|
@@ -765,6 +768,7 @@ class Icpay {
|
|
|
765
768
|
paymentIntentId: paymentIntentId,
|
|
766
769
|
maxAttempts: 120,
|
|
767
770
|
delayMs: 1000,
|
|
771
|
+
icpayPaymentLink: request?.metadata?.icpayPaymentLink,
|
|
768
772
|
});
|
|
769
773
|
// Derive status from API response
|
|
770
774
|
let statusString = 'pending';
|
|
@@ -1007,12 +1011,11 @@ class Icpay {
|
|
|
1007
1011
|
}
|
|
1008
1012
|
if (!signedTxB64 && !signerSigBase58)
|
|
1009
1013
|
throw new Error('Wallet did not return a signed transaction');
|
|
1010
|
-
// Relay via API
|
|
1014
|
+
// Relay via API. Standard flow prebuilt tx has payer as fee payer (no facilitatorPaysFee).
|
|
1011
1015
|
if (signedTxB64) {
|
|
1012
1016
|
relay = await this.publicApiClient.post('/sdk/public/payments/solana/relay', {
|
|
1013
1017
|
signedTransactionBase64: signedTxB64,
|
|
1014
1018
|
paymentIntentId: params.paymentIntentId,
|
|
1015
|
-
facilitatorPaysFee: true,
|
|
1016
1019
|
});
|
|
1017
1020
|
}
|
|
1018
1021
|
else {
|
|
@@ -1100,7 +1103,7 @@ class Icpay {
|
|
|
1100
1103
|
return out;
|
|
1101
1104
|
}
|
|
1102
1105
|
try {
|
|
1103
|
-
await this.performNotifyPaymentIntent({ paymentIntentId: params.paymentIntentId, transactionId: signature, maxAttempts: 1 });
|
|
1106
|
+
await this.performNotifyPaymentIntent({ paymentIntentId: params.paymentIntentId, transactionId: signature, maxAttempts: 1, icpayPaymentLink: params.metadata?.icpayPaymentLink });
|
|
1104
1107
|
}
|
|
1105
1108
|
catch { }
|
|
1106
1109
|
const finalQuick = await this.awaitIntentTerminal({
|
|
@@ -1290,7 +1293,7 @@ class Icpay {
|
|
|
1290
1293
|
catch { }
|
|
1291
1294
|
// Inform API immediately with tx hash so it can start indexing
|
|
1292
1295
|
try {
|
|
1293
|
-
await this.performNotifyPaymentIntent({ paymentIntentId: params.paymentIntentId, transactionId: txHash, maxAttempts: 1 });
|
|
1296
|
+
await this.performNotifyPaymentIntent({ paymentIntentId: params.paymentIntentId, transactionId: txHash, maxAttempts: 1, icpayPaymentLink: params.metadata?.icpayPaymentLink });
|
|
1294
1297
|
}
|
|
1295
1298
|
catch { }
|
|
1296
1299
|
const finalResponse = await this.awaitIntentTerminal({
|
|
@@ -1426,6 +1429,40 @@ class Icpay {
|
|
|
1426
1429
|
}
|
|
1427
1430
|
return new Uint8Array(out);
|
|
1428
1431
|
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Resolve payment intent from config or request: use full intent if provided, else fetch by id via API.
|
|
1434
|
+
* Returns null when no pre-provided intent (caller should create via POST).
|
|
1435
|
+
*/
|
|
1436
|
+
async getOrResolvePaymentIntent(request) {
|
|
1437
|
+
const isFullIntent = (o) => o && typeof o === 'object' && o.id && typeof (o.amount === 'string' ? o.amount : String(o.amount ?? '')) === 'string' &&
|
|
1438
|
+
(o.chainType != null || o.chainId != null || (o.ledgerCanisterId != null && o.ledgerCanisterId !== ''));
|
|
1439
|
+
const fromRequest = request?.paymentIntent;
|
|
1440
|
+
const fromConfig = this.config?.paymentIntent;
|
|
1441
|
+
if (fromRequest && isFullIntent(fromRequest)) {
|
|
1442
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'using payment intent from request');
|
|
1443
|
+
return { paymentIntent: fromRequest, onramp: null };
|
|
1444
|
+
}
|
|
1445
|
+
if (fromConfig && isFullIntent(fromConfig)) {
|
|
1446
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'using payment intent from config');
|
|
1447
|
+
return { paymentIntent: fromConfig, onramp: null };
|
|
1448
|
+
}
|
|
1449
|
+
const intentId = (fromRequest && typeof fromRequest.id === 'string' && fromRequest.id) ||
|
|
1450
|
+
(fromConfig && typeof fromConfig.id === 'string' && fromConfig.id) ||
|
|
1451
|
+
(typeof this.config?.paymentIntentId === 'string' && this.config.paymentIntentId) || null;
|
|
1452
|
+
if (intentId && this.publicApiClient) {
|
|
1453
|
+
try {
|
|
1454
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'fetching payment intent by id', { intentId });
|
|
1455
|
+
const resp = await this.publicApiClient.get(`/sdk/public/payments/intents/${encodeURIComponent(intentId)}`);
|
|
1456
|
+
if (resp?.paymentIntent) {
|
|
1457
|
+
return { paymentIntent: resp.paymentIntent, onramp: resp.onramp ?? null };
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
catch (e) {
|
|
1461
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'fetch payment intent failed', e);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
return null;
|
|
1465
|
+
}
|
|
1429
1466
|
/**
|
|
1430
1467
|
* Create a payment to a specific canister/ledger (public method)
|
|
1431
1468
|
* This is now a real transaction
|
|
@@ -1438,17 +1475,18 @@ class Icpay {
|
|
|
1438
1475
|
let ledgerCanisterId = request.ledgerCanisterId;
|
|
1439
1476
|
const tokenShortcode = request?.tokenShortcode;
|
|
1440
1477
|
const isOnrampFlow = (request?.onrampPayment === true) || (this?.config?.onrampPayment === true);
|
|
1441
|
-
|
|
1478
|
+
const hasPaymentIntent = request?.paymentIntent != null || this.config?.paymentIntent != null || (typeof this.config?.paymentIntentId === 'string' && this.config.paymentIntentId);
|
|
1479
|
+
if (!ledgerCanisterId && !tokenShortcode && !request.symbol && !isOnrampFlow && !hasPaymentIntent) {
|
|
1442
1480
|
const err = new errors_1.IcpayError({
|
|
1443
1481
|
code: errors_1.ICPAY_ERROR_CODES.INVALID_CONFIG,
|
|
1444
|
-
message: 'Provide either tokenShortcode or ledgerCanisterId (symbol is deprecated).',
|
|
1482
|
+
message: 'Provide either tokenShortcode or ledgerCanisterId (symbol is deprecated), or pass paymentIntent.',
|
|
1445
1483
|
details: { request }
|
|
1446
1484
|
});
|
|
1447
1485
|
this.emitMethodError('createPayment', err);
|
|
1448
1486
|
throw err;
|
|
1449
1487
|
}
|
|
1450
1488
|
let memo = undefined;
|
|
1451
|
-
// 1)
|
|
1489
|
+
// 1) Resolve or create payment intent: use pre-provided (config/request) or fetch by id, else create via API
|
|
1452
1490
|
let paymentIntentId = null;
|
|
1453
1491
|
let paymentIntentCode = null;
|
|
1454
1492
|
let intentChainType;
|
|
@@ -1456,111 +1494,162 @@ class Icpay {
|
|
|
1456
1494
|
let accountCanisterId;
|
|
1457
1495
|
let resolvedAmountStr = typeof request.amount === 'string' ? request.amount : (request.amount != null ? String(request.amount) : undefined);
|
|
1458
1496
|
let intentResp;
|
|
1497
|
+
let expectedSenderPrincipal;
|
|
1459
1498
|
try {
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
this.
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
const evm = this.config?.evmProvider || globalThis?.ethereum;
|
|
1478
|
-
if (evm?.request) {
|
|
1479
|
-
try {
|
|
1480
|
-
const accounts = await evm.request({ method: 'eth_accounts' });
|
|
1481
|
-
if (Array.isArray(accounts) && accounts[0]) {
|
|
1482
|
-
const lowerAccounts = accounts.map((a) => String(a).toLowerCase());
|
|
1483
|
-
const providedRaw = request?.expectedSenderPrincipal;
|
|
1484
|
-
if (providedRaw) {
|
|
1485
|
-
const provided = String(providedRaw).toLowerCase();
|
|
1486
|
-
expectedSenderPrincipal = lowerAccounts.includes(provided) ? accounts[lowerAccounts.indexOf(provided)] : accounts[0];
|
|
1499
|
+
const meta = request?.metadata || {};
|
|
1500
|
+
const isAtxp = Boolean(meta?.icpay_atxp_request) && typeof (meta?.atxp_request_id) === 'string';
|
|
1501
|
+
const onramp = (request.onrampPayment === true || this.config.onrampPayment === true) && this.config.onrampDisabled !== true ? true : false;
|
|
1502
|
+
// If we have a pre-provided intent or an id to fetch, resolve first (skip create for non-ATXP/non-onramp create flows)
|
|
1503
|
+
if (!isAtxp && !onramp) {
|
|
1504
|
+
const resolved = await this.getOrResolvePaymentIntent(request);
|
|
1505
|
+
if (resolved) {
|
|
1506
|
+
let pi = resolved.paymentIntent;
|
|
1507
|
+
const intentHasToken = (pi?.ledgerId != null) || (pi?.ledgerCanisterId && String(pi.ledgerCanisterId).trim() !== '');
|
|
1508
|
+
if (!intentHasToken) {
|
|
1509
|
+
const tokenToSet = request?.tokenShortcode;
|
|
1510
|
+
if (tokenToSet && typeof tokenToSet === 'string' && this.publicApiClient) {
|
|
1511
|
+
try {
|
|
1512
|
+
await this.publicApiClient.patch(`/sdk/public/payments/intents/${encodeURIComponent(pi.id)}/set-token`, { tokenShortcode: tokenToSet.trim() });
|
|
1513
|
+
const refetched = await this.publicApiClient.get(`/sdk/public/payments/intents/${encodeURIComponent(pi.id)}`);
|
|
1514
|
+
if (refetched?.paymentIntent)
|
|
1515
|
+
pi = refetched.paymentIntent;
|
|
1487
1516
|
}
|
|
1488
|
-
|
|
1489
|
-
|
|
1517
|
+
catch (e) {
|
|
1518
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'set-token failed', e);
|
|
1519
|
+
throw new errors_1.IcpayError({
|
|
1520
|
+
code: errors_1.ICPAY_ERROR_CODES.API_ERROR,
|
|
1521
|
+
message: 'Payment intent requires a token to be selected. Setting token failed.',
|
|
1522
|
+
details: e,
|
|
1523
|
+
retryable: true,
|
|
1524
|
+
userAction: 'Ensure tokenShortcode is valid and try again',
|
|
1525
|
+
});
|
|
1490
1526
|
}
|
|
1491
1527
|
}
|
|
1528
|
+
else {
|
|
1529
|
+
throw new errors_1.IcpayError({
|
|
1530
|
+
code: errors_1.ICPAY_ERROR_CODES.INVALID_CONFIG,
|
|
1531
|
+
message: 'Payment intent requires a token to be selected before payment. Provide tokenShortcode in the request.',
|
|
1532
|
+
details: { paymentIntentId: pi?.id },
|
|
1533
|
+
retryable: false,
|
|
1534
|
+
userAction: 'Select a token (e.g. pass tokenShortcode) and try again',
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1492
1537
|
}
|
|
1493
|
-
|
|
1538
|
+
intentResp = { paymentIntent: pi, onramp: resolved.onramp };
|
|
1539
|
+
expectedSenderPrincipal = request.expectedSenderPrincipal ||
|
|
1540
|
+
this.connectedWallet?.owner || this.connectedWallet?.principal?.toString();
|
|
1494
1541
|
}
|
|
1495
1542
|
}
|
|
1496
|
-
if (!
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
if (
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
recipientAddresses: request?.recipientAddresses || undefined,
|
|
1543
|
+
if (!intentResp) {
|
|
1544
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'creating payment intent via API');
|
|
1545
|
+
// Resolve expected sender principal:
|
|
1546
|
+
// Start with any value explicitly provided on the request or via connectedWallet.
|
|
1547
|
+
expectedSenderPrincipal =
|
|
1548
|
+
request.expectedSenderPrincipal ||
|
|
1549
|
+
this.connectedWallet?.owner ||
|
|
1550
|
+
this.connectedWallet?.principal?.toString();
|
|
1551
|
+
// If none yet and a Solana provider is present (e.g., Phantom), prefer its publicKey (base58).
|
|
1552
|
+
try {
|
|
1553
|
+
const solProv = this.config?.solanaProvider || globalThis?.solana;
|
|
1554
|
+
const solPk = solProv?.publicKey ? String(solProv.publicKey) : undefined;
|
|
1555
|
+
if (!expectedSenderPrincipal && solPk) {
|
|
1556
|
+
expectedSenderPrincipal = String(solPk);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
catch { }
|
|
1560
|
+
// Only if still missing, fall back to EVM accounts.
|
|
1561
|
+
if (!expectedSenderPrincipal) {
|
|
1562
|
+
const evm = this.config?.evmProvider || globalThis?.ethereum;
|
|
1563
|
+
if (evm?.request) {
|
|
1564
|
+
try {
|
|
1565
|
+
const accounts = await evm.request({ method: 'eth_accounts' });
|
|
1566
|
+
if (Array.isArray(accounts) && accounts[0]) {
|
|
1567
|
+
const lowerAccounts = accounts.map((a) => String(a).toLowerCase());
|
|
1568
|
+
const providedRaw = request?.expectedSenderPrincipal;
|
|
1569
|
+
if (providedRaw) {
|
|
1570
|
+
const provided = String(providedRaw).toLowerCase();
|
|
1571
|
+
expectedSenderPrincipal = lowerAccounts.includes(provided) ? accounts[lowerAccounts.indexOf(provided)] : accounts[0];
|
|
1572
|
+
}
|
|
1573
|
+
else {
|
|
1574
|
+
expectedSenderPrincipal = accounts[0];
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
catch { }
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
if (!expectedSenderPrincipal && !((request?.onrampPayment === true) || (this?.config?.onrampPayment === true))) {
|
|
1582
|
+
throw new errors_1.IcpayError({
|
|
1583
|
+
code: errors_1.ICPAY_ERROR_CODES.WALLET_NOT_CONNECTED,
|
|
1584
|
+
message: 'Wallet must be connected to create payment intent',
|
|
1585
|
+
details: { connectedWallet: this.connectedWallet },
|
|
1586
|
+
retryable: false,
|
|
1587
|
+
userAction: 'Connect your wallet first'
|
|
1542
1588
|
});
|
|
1543
1589
|
}
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1590
|
+
const onramp = (request.onrampPayment === true || this.config.onrampPayment === true) && this.config.onrampDisabled !== true ? true : false;
|
|
1591
|
+
const meta = request?.metadata || {};
|
|
1592
|
+
const isAtxp = Boolean(meta?.icpay_atxp_request) && typeof (meta?.atxp_request_id) === 'string';
|
|
1593
|
+
// Resolve recipientAddress only for non-onramp flows
|
|
1594
|
+
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
1595
|
+
const reqAny = request;
|
|
1596
|
+
const addrObj = (reqAny?.recipientAddresses) || {};
|
|
1597
|
+
const candidateEvm = addrObj.evm ? addrObj.evm : undefined;
|
|
1598
|
+
const candidateIC = addrObj.ic ? addrObj.ic : undefined;
|
|
1599
|
+
const candidateSol = addrObj.sol ? addrObj.sol : undefined;
|
|
1600
|
+
let recipientAddress = undefined;
|
|
1601
|
+
if (!onramp) {
|
|
1602
|
+
// Choose a default to persist on the intent; EVM will override to ZERO if non-hex when building tx
|
|
1603
|
+
recipientAddress = (reqAny?.recipientAddress) || candidateEvm || candidateIC || candidateSol || ZERO_ADDRESS;
|
|
1604
|
+
(0, utils_1.debugLog)(this.config.debug || false, 'recipientAddress resolved for intent', { recipientAddress });
|
|
1605
|
+
}
|
|
1606
|
+
if (isAtxp) {
|
|
1607
|
+
// Route ATXP intents to the ATXP endpoint so they link to the request
|
|
1608
|
+
const atxpRequestId = String(meta.atxp_request_id);
|
|
1609
|
+
const endpoint = `/sdk/public/atxp/requests/${encodeURIComponent(atxpRequestId)}/payment-intents`;
|
|
1610
|
+
intentResp = await this.publicApiClient.post(endpoint, {
|
|
1548
1611
|
tokenShortcode: tokenShortcode || undefined,
|
|
1549
|
-
// Legacy fields for backwards compatibility
|
|
1550
|
-
symbol: tokenShortcode ? undefined : request.symbol,
|
|
1551
|
-
ledgerCanisterId: tokenShortcode ? undefined : ledgerCanisterId,
|
|
1552
1612
|
description: request.description,
|
|
1553
|
-
expectedSenderPrincipal,
|
|
1554
|
-
metadata: normalizeSdkMetadata(request.metadata || {}),
|
|
1555
|
-
amountUsd: request.amountUsd,
|
|
1556
|
-
// With tokenShortcode, backend derives chain. Keep legacy chainId for old flows.
|
|
1557
|
-
chainId: tokenShortcode ? undefined : request.chainId,
|
|
1558
|
-
widgetParams: request.widgetParams || undefined,
|
|
1559
1613
|
recipientAddress,
|
|
1560
1614
|
recipientAddresses: request?.recipientAddresses || undefined,
|
|
1561
1615
|
externalCostAmount: request?.externalCostAmount ?? request?.metadata?.externalCostAmount ?? undefined,
|
|
1616
|
+
fiat_currency: request?.fiat_currency,
|
|
1562
1617
|
});
|
|
1563
1618
|
}
|
|
1619
|
+
else {
|
|
1620
|
+
if (onramp) {
|
|
1621
|
+
// Route onramp flows to the dedicated onramp endpoint without requiring token/ledger
|
|
1622
|
+
intentResp = await this.publicApiClient.post('/sdk/public/onramp/intents', {
|
|
1623
|
+
usdAmount: request.amountUsd,
|
|
1624
|
+
description: request.description,
|
|
1625
|
+
metadata: normalizeSdkMetadata(request.metadata || {}),
|
|
1626
|
+
widgetParams: request.widgetParams || undefined,
|
|
1627
|
+
recipientAddresses: request?.recipientAddresses || undefined,
|
|
1628
|
+
fiat_currency: request?.fiat_currency,
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
else {
|
|
1632
|
+
intentResp = await this.publicApiClient.post('/sdk/public/payments/intents', {
|
|
1633
|
+
amount: (typeof request.amount === 'string' ? request.amount : (request.amount != null ? String(request.amount) : undefined)),
|
|
1634
|
+
// Prefer tokenShortcode if provided
|
|
1635
|
+
tokenShortcode: tokenShortcode || undefined,
|
|
1636
|
+
// Legacy fields for backwards compatibility
|
|
1637
|
+
symbol: tokenShortcode ? undefined : request.symbol,
|
|
1638
|
+
ledgerCanisterId: tokenShortcode ? undefined : ledgerCanisterId,
|
|
1639
|
+
description: request.description,
|
|
1640
|
+
expectedSenderPrincipal,
|
|
1641
|
+
metadata: normalizeSdkMetadata(request.metadata || {}),
|
|
1642
|
+
amountUsd: request.amountUsd,
|
|
1643
|
+
// With tokenShortcode, backend derives chain. Keep legacy chainId for old flows.
|
|
1644
|
+
chainId: tokenShortcode ? undefined : request.chainId,
|
|
1645
|
+
widgetParams: request.widgetParams || undefined,
|
|
1646
|
+
recipientAddress,
|
|
1647
|
+
recipientAddresses: request?.recipientAddresses || undefined,
|
|
1648
|
+
externalCostAmount: request?.externalCostAmount ?? request?.metadata?.externalCostAmount ?? undefined,
|
|
1649
|
+
fiat_currency: request?.fiat_currency,
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1564
1653
|
}
|
|
1565
1654
|
paymentIntentId = intentResp?.paymentIntent?.id || null;
|
|
1566
1655
|
paymentIntentCode = intentResp?.paymentIntent?.intentCode ?? null;
|
|
@@ -1974,6 +2063,8 @@ class Icpay {
|
|
|
1974
2063
|
search.set('amountUsd', String(params.amountUsd));
|
|
1975
2064
|
if (typeof params.amount === 'string' && params.amount)
|
|
1976
2065
|
search.set('amount', params.amount);
|
|
2066
|
+
if (typeof params.fiatCurrency === 'string' && params.fiatCurrency.trim())
|
|
2067
|
+
search.set('fiatCurrency', params.fiatCurrency.trim());
|
|
1977
2068
|
if (Array.isArray(params.chainShortcodes) && params.chainShortcodes.length > 0)
|
|
1978
2069
|
search.set('chainShortcodes', params.chainShortcodes.join(','));
|
|
1979
2070
|
if (Array.isArray(params.tokenShortcodes) && params.tokenShortcodes.length > 0)
|
|
@@ -2215,6 +2306,7 @@ class Icpay {
|
|
|
2215
2306
|
chainId: tokenShortcode ? undefined : request.chainId,
|
|
2216
2307
|
recipientAddress: request?.recipientAddress || '0x0000000000000000000000000000000000000000',
|
|
2217
2308
|
recipientAddresses: request?.recipientAddresses,
|
|
2309
|
+
fiat_currency: request?.fiat_currency ?? request?.fiatCurrency ?? this.config?.fiat_currency,
|
|
2218
2310
|
};
|
|
2219
2311
|
const res = await this.createPayment(createTransactionRequest);
|
|
2220
2312
|
this.emitMethodSuccess('createPaymentUsd', res);
|
|
@@ -2246,6 +2338,12 @@ class Icpay {
|
|
|
2246
2338
|
// We forward both amountUsd and amount (if provided), and do not resolve canister here.
|
|
2247
2339
|
const ledgerCanisterId = request.ledgerCanisterId || '';
|
|
2248
2340
|
const tokenShortcode = request?.tokenShortcode;
|
|
2341
|
+
// Resolve existing payment intent id so API reuses it instead of creating a second intent (e.g. pay link page)
|
|
2342
|
+
const existingIntentId = (typeof request?.paymentIntentId === 'string' && request.paymentIntentId) ||
|
|
2343
|
+
(typeof request?.paymentIntent?.id === 'string' && request.paymentIntent.id) ||
|
|
2344
|
+
(typeof this.config?.paymentIntentId === 'string' && this.config.paymentIntentId) ||
|
|
2345
|
+
(typeof this.config?.paymentIntent?.id === 'string' && this.config.paymentIntent.id) ||
|
|
2346
|
+
null;
|
|
2249
2347
|
// Hit X402 endpoint
|
|
2250
2348
|
const body = {
|
|
2251
2349
|
amount: request.amount,
|
|
@@ -2259,7 +2357,14 @@ class Icpay {
|
|
|
2259
2357
|
chainId: tokenShortcode ? undefined : request.chainId,
|
|
2260
2358
|
x402: true,
|
|
2261
2359
|
recipientAddress: request?.recipientAddress || '0x0000000000000000000000000000000000000000',
|
|
2360
|
+
fiat_currency: request?.fiat_currency ?? request?.fiatCurrency ?? this.config?.fiat_currency,
|
|
2262
2361
|
};
|
|
2362
|
+
if (existingIntentId) {
|
|
2363
|
+
body.paymentIntentId = existingIntentId;
|
|
2364
|
+
}
|
|
2365
|
+
if (body.fiat_currency === undefined || body.fiat_currency === '') {
|
|
2366
|
+
delete body.fiat_currency;
|
|
2367
|
+
}
|
|
2263
2368
|
// Include Solana payerPublicKey so server can build unsigned tx (standard x402 flow)
|
|
2264
2369
|
try {
|
|
2265
2370
|
const w = globalThis?.window || globalThis;
|
|
@@ -2580,7 +2685,7 @@ class Icpay {
|
|
|
2580
2685
|
catch { }
|
|
2581
2686
|
if (sig) {
|
|
2582
2687
|
try {
|
|
2583
|
-
await this.performNotifyPaymentIntent({ paymentIntentId, transactionId: sig, maxAttempts: 1 });
|
|
2688
|
+
await this.performNotifyPaymentIntent({ paymentIntentId, transactionId: sig, maxAttempts: 1, icpayPaymentLink: request.metadata?.icpayPaymentLink });
|
|
2584
2689
|
}
|
|
2585
2690
|
catch { }
|
|
2586
2691
|
}
|
|
@@ -3848,6 +3953,8 @@ class Icpay {
|
|
|
3848
3953
|
body.externalCostAmount = params.externalCostAmount;
|
|
3849
3954
|
if (typeof params.recipientPrincipal === 'string')
|
|
3850
3955
|
body.recipientPrincipal = params.recipientPrincipal;
|
|
3956
|
+
if (params.icpayPaymentLink && typeof params.icpayPaymentLink === 'object')
|
|
3957
|
+
body.icpayPaymentLink = params.icpayPaymentLink;
|
|
3851
3958
|
const resp = await notifyClient.post(notifyPath, body);
|
|
3852
3959
|
// If this is the last attempt, return whatever we got
|
|
3853
3960
|
if (attempt === maxAttempts) {
|
|
@@ -3899,6 +4006,7 @@ class Icpay {
|
|
|
3899
4006
|
accountCanisterId: params.accountCanisterId,
|
|
3900
4007
|
externalCostAmount: params.externalCostAmount,
|
|
3901
4008
|
recipientPrincipal: params.recipientPrincipal,
|
|
4009
|
+
icpayPaymentLink: params.metadata?.icpayPaymentLink,
|
|
3902
4010
|
});
|
|
3903
4011
|
const status = resp?.paymentIntent?.status || resp?.payment?.status || resp?.status || '';
|
|
3904
4012
|
const norm = typeof status === 'string' ? status.toLowerCase() : '';
|