@zendfi/sdk 0.6.0 → 0.7.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 +111 -8
- package/dist/express.d.mts +1 -1
- package/dist/express.d.ts +1 -1
- package/dist/index.d.mts +208 -3
- package/dist/index.d.ts +208 -3
- package/dist/index.js +679 -6
- package/dist/index.mjs +678 -6
- package/dist/nextjs.d.mts +1 -1
- package/dist/nextjs.d.ts +1 -1
- package/dist/{webhook-handler-DGBeCWT-.d.mts → webhook-handler-D5INiR-l.d.mts} +1 -1
- package/dist/{webhook-handler-DGBeCWT-.d.ts → webhook-handler-D5INiR-l.d.ts} +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1041,6 +1041,7 @@ __export(index_exports, {
|
|
|
1041
1041
|
WalletConnector: () => WalletConnector,
|
|
1042
1042
|
WebhookError: () => WebhookError,
|
|
1043
1043
|
ZendFiClient: () => ZendFiClient,
|
|
1044
|
+
ZendFiEmbeddedCheckout: () => ZendFiEmbeddedCheckout,
|
|
1044
1045
|
ZendFiError: () => ZendFiError2,
|
|
1045
1046
|
ZendFiSessionKeyManager: () => ZendFiSessionKeyManager,
|
|
1046
1047
|
asAgentKeyId: () => asAgentKeyId,
|
|
@@ -1525,7 +1526,7 @@ function createInterceptors() {
|
|
|
1525
1526
|
};
|
|
1526
1527
|
}
|
|
1527
1528
|
|
|
1528
|
-
// src/
|
|
1529
|
+
// src/aip/agent.ts
|
|
1529
1530
|
function normalizeArrayResponse(response, key) {
|
|
1530
1531
|
if (Array.isArray(response)) {
|
|
1531
1532
|
return response;
|
|
@@ -1793,7 +1794,7 @@ var AgentAPI = class {
|
|
|
1793
1794
|
}
|
|
1794
1795
|
};
|
|
1795
1796
|
|
|
1796
|
-
// src/
|
|
1797
|
+
// src/aip/intents.ts
|
|
1797
1798
|
var PaymentIntentsAPI = class {
|
|
1798
1799
|
constructor(request) {
|
|
1799
1800
|
this.request = request;
|
|
@@ -1956,7 +1957,7 @@ var PaymentIntentsAPI = class {
|
|
|
1956
1957
|
}
|
|
1957
1958
|
};
|
|
1958
1959
|
|
|
1959
|
-
// src/
|
|
1960
|
+
// src/aip/pricing.ts
|
|
1960
1961
|
var PricingAPI = class {
|
|
1961
1962
|
constructor(request) {
|
|
1962
1963
|
this.request = request;
|
|
@@ -2093,7 +2094,7 @@ var PricingAPI = class {
|
|
|
2093
2094
|
}
|
|
2094
2095
|
};
|
|
2095
2096
|
|
|
2096
|
-
// src/
|
|
2097
|
+
// src/aip/autonomy.ts
|
|
2097
2098
|
var AutonomyAPI = class {
|
|
2098
2099
|
constructor(request) {
|
|
2099
2100
|
this.request = request;
|
|
@@ -2305,7 +2306,7 @@ var AutonomyAPI = class {
|
|
|
2305
2306
|
}
|
|
2306
2307
|
};
|
|
2307
2308
|
|
|
2308
|
-
// src/
|
|
2309
|
+
// src/aip/smart-payments.ts
|
|
2309
2310
|
var SmartPaymentsAPI = class {
|
|
2310
2311
|
constructor(request) {
|
|
2311
2312
|
this.request = request;
|
|
@@ -2410,7 +2411,7 @@ var SmartPaymentsAPI = class {
|
|
|
2410
2411
|
}
|
|
2411
2412
|
};
|
|
2412
2413
|
|
|
2413
|
-
// src/
|
|
2414
|
+
// src/aip/session-keys.ts
|
|
2414
2415
|
var SessionKeysAPI = class {
|
|
2415
2416
|
constructor(request) {
|
|
2416
2417
|
this.request = request;
|
|
@@ -3490,6 +3491,677 @@ function verifyWebhookSignature(payload, signature, secret) {
|
|
|
3490
3491
|
});
|
|
3491
3492
|
}
|
|
3492
3493
|
|
|
3494
|
+
// src/embedded-checkout.ts
|
|
3495
|
+
var ZendFiEmbeddedCheckout = class {
|
|
3496
|
+
config;
|
|
3497
|
+
container = null;
|
|
3498
|
+
checkoutData = null;
|
|
3499
|
+
pollInterval = null;
|
|
3500
|
+
mounted = false;
|
|
3501
|
+
constructor(config) {
|
|
3502
|
+
this.config = {
|
|
3503
|
+
linkCode: config.linkCode || "",
|
|
3504
|
+
paymentId: config.paymentId || "",
|
|
3505
|
+
containerId: config.containerId,
|
|
3506
|
+
mode: config.mode || "test",
|
|
3507
|
+
apiUrl: config.apiUrl || this.getDefaultApiUrl(),
|
|
3508
|
+
onSuccess: config.onSuccess || (() => {
|
|
3509
|
+
}),
|
|
3510
|
+
onError: config.onError || (() => {
|
|
3511
|
+
}),
|
|
3512
|
+
onLoad: config.onLoad || (() => {
|
|
3513
|
+
}),
|
|
3514
|
+
theme: config.theme || {},
|
|
3515
|
+
allowCustomAmount: config.allowCustomAmount || false,
|
|
3516
|
+
paymentMethods: config.paymentMethods || {
|
|
3517
|
+
walletConnect: true,
|
|
3518
|
+
qrCode: true,
|
|
3519
|
+
solanaWallet: true
|
|
3520
|
+
}
|
|
3521
|
+
};
|
|
3522
|
+
if (!this.config.linkCode && !this.config.paymentId) {
|
|
3523
|
+
throw new Error("Either linkCode or paymentId must be provided");
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
getDefaultApiUrl() {
|
|
3527
|
+
if (typeof window !== "undefined" && window.location.hostname === "localhost") {
|
|
3528
|
+
return "http://localhost:8080";
|
|
3529
|
+
}
|
|
3530
|
+
return "https://api.zendfi.tech";
|
|
3531
|
+
}
|
|
3532
|
+
/**
|
|
3533
|
+
* Mount the checkout to the DOM
|
|
3534
|
+
*/
|
|
3535
|
+
async mount() {
|
|
3536
|
+
if (this.mounted) {
|
|
3537
|
+
console.warn("Checkout is already mounted");
|
|
3538
|
+
return;
|
|
3539
|
+
}
|
|
3540
|
+
this.container = document.getElementById(this.config.containerId);
|
|
3541
|
+
if (!this.container) {
|
|
3542
|
+
throw new Error(`Container element #${this.config.containerId} not found`);
|
|
3543
|
+
}
|
|
3544
|
+
try {
|
|
3545
|
+
this.renderLoading();
|
|
3546
|
+
await this.loadDependencies();
|
|
3547
|
+
await this.fetchCheckoutData();
|
|
3548
|
+
this.render();
|
|
3549
|
+
this.startPaymentPolling();
|
|
3550
|
+
this.mounted = true;
|
|
3551
|
+
this.config.onLoad();
|
|
3552
|
+
} catch (error) {
|
|
3553
|
+
const err = error;
|
|
3554
|
+
this.config.onError({
|
|
3555
|
+
code: "MOUNT_ERROR",
|
|
3556
|
+
message: err.message,
|
|
3557
|
+
details: error
|
|
3558
|
+
});
|
|
3559
|
+
this.renderError(err.message);
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
/**
|
|
3563
|
+
* Unmount and cleanup
|
|
3564
|
+
*/
|
|
3565
|
+
unmount() {
|
|
3566
|
+
if (this.pollInterval) {
|
|
3567
|
+
clearInterval(this.pollInterval);
|
|
3568
|
+
this.pollInterval = null;
|
|
3569
|
+
}
|
|
3570
|
+
if (this.container) {
|
|
3571
|
+
this.container.innerHTML = "";
|
|
3572
|
+
}
|
|
3573
|
+
this.mounted = false;
|
|
3574
|
+
}
|
|
3575
|
+
/**
|
|
3576
|
+
* Fetch checkout data from API
|
|
3577
|
+
*/
|
|
3578
|
+
async fetchCheckoutData() {
|
|
3579
|
+
let endpoint;
|
|
3580
|
+
let method;
|
|
3581
|
+
if (this.config.linkCode) {
|
|
3582
|
+
endpoint = `/api/v1/payment-links/${this.config.linkCode}/pay`;
|
|
3583
|
+
method = "POST";
|
|
3584
|
+
} else {
|
|
3585
|
+
endpoint = `/api/v1/payments/${this.config.paymentId}/checkout-data`;
|
|
3586
|
+
method = "GET";
|
|
3587
|
+
}
|
|
3588
|
+
const response = await fetch(`${this.config.apiUrl}${endpoint}`, {
|
|
3589
|
+
method,
|
|
3590
|
+
headers: {
|
|
3591
|
+
"Content-Type": "application/json"
|
|
3592
|
+
}
|
|
3593
|
+
});
|
|
3594
|
+
if (!response.ok) {
|
|
3595
|
+
throw new Error(`Failed to fetch checkout data: ${response.statusText}`);
|
|
3596
|
+
}
|
|
3597
|
+
this.checkoutData = await response.json();
|
|
3598
|
+
}
|
|
3599
|
+
/**
|
|
3600
|
+
* Poll for payment confirmation
|
|
3601
|
+
*/
|
|
3602
|
+
startPaymentPolling() {
|
|
3603
|
+
this.pollInterval = setInterval(async () => {
|
|
3604
|
+
if (!this.checkoutData) return;
|
|
3605
|
+
try {
|
|
3606
|
+
const response = await fetch(
|
|
3607
|
+
`${this.config.apiUrl}/api/v1/payments/${this.checkoutData.payment_id}/status`,
|
|
3608
|
+
{
|
|
3609
|
+
method: "GET",
|
|
3610
|
+
headers: {
|
|
3611
|
+
"Content-Type": "application/json"
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
);
|
|
3615
|
+
if (response.ok) {
|
|
3616
|
+
const data = await response.json();
|
|
3617
|
+
if (data.status === "confirmed") {
|
|
3618
|
+
this.handlePaymentSuccess(data);
|
|
3619
|
+
} else if (data.status === "failed") {
|
|
3620
|
+
this.handlePaymentFailure(data);
|
|
3621
|
+
} else if (data.status === "expired") {
|
|
3622
|
+
this.handlePaymentExpired();
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
} catch (error) {
|
|
3626
|
+
console.error("Payment status check failed:", error);
|
|
3627
|
+
}
|
|
3628
|
+
}, 3e3);
|
|
3629
|
+
}
|
|
3630
|
+
/**
|
|
3631
|
+
* Handle successful payment
|
|
3632
|
+
*/
|
|
3633
|
+
handlePaymentSuccess(data) {
|
|
3634
|
+
if (this.pollInterval) {
|
|
3635
|
+
clearInterval(this.pollInterval);
|
|
3636
|
+
this.pollInterval = null;
|
|
3637
|
+
}
|
|
3638
|
+
this.config.onSuccess({
|
|
3639
|
+
paymentId: data.id,
|
|
3640
|
+
transactionSignature: data.transaction_signature,
|
|
3641
|
+
amount: data.amount_usd || data.amount,
|
|
3642
|
+
token: data.payment_token,
|
|
3643
|
+
merchantName: this.checkoutData?.merchant_name || "Merchant"
|
|
3644
|
+
});
|
|
3645
|
+
this.renderSuccess();
|
|
3646
|
+
}
|
|
3647
|
+
/**
|
|
3648
|
+
* Handle payment failure
|
|
3649
|
+
*/
|
|
3650
|
+
handlePaymentFailure(data) {
|
|
3651
|
+
if (this.pollInterval) {
|
|
3652
|
+
clearInterval(this.pollInterval);
|
|
3653
|
+
this.pollInterval = null;
|
|
3654
|
+
}
|
|
3655
|
+
this.config.onError({
|
|
3656
|
+
code: "PAYMENT_FAILED",
|
|
3657
|
+
message: "Payment failed",
|
|
3658
|
+
details: data
|
|
3659
|
+
});
|
|
3660
|
+
this.renderError("Payment failed. Please try again.");
|
|
3661
|
+
}
|
|
3662
|
+
/**
|
|
3663
|
+
* Handle payment expiration
|
|
3664
|
+
*/
|
|
3665
|
+
handlePaymentExpired() {
|
|
3666
|
+
if (this.pollInterval) {
|
|
3667
|
+
clearInterval(this.pollInterval);
|
|
3668
|
+
this.pollInterval = null;
|
|
3669
|
+
}
|
|
3670
|
+
this.config.onError({
|
|
3671
|
+
code: "PAYMENT_EXPIRED",
|
|
3672
|
+
message: "Payment link has expired"
|
|
3673
|
+
});
|
|
3674
|
+
this.renderError("This payment link has expired.");
|
|
3675
|
+
}
|
|
3676
|
+
/**
|
|
3677
|
+
* Render loading state
|
|
3678
|
+
*/
|
|
3679
|
+
renderLoading() {
|
|
3680
|
+
if (!this.container) return;
|
|
3681
|
+
this.container.innerHTML = `
|
|
3682
|
+
<div class="zendfi-checkout-loading" style="${this.getLoadingStyles()}">
|
|
3683
|
+
<div class="zendfi-spinner" style="${this.getSpinnerStyles()}"></div>
|
|
3684
|
+
<p style="margin-top: 16px; color: #666;">Loading checkout...</p>
|
|
3685
|
+
</div>
|
|
3686
|
+
`;
|
|
3687
|
+
}
|
|
3688
|
+
/**
|
|
3689
|
+
* Render the main checkout UI
|
|
3690
|
+
*/
|
|
3691
|
+
render() {
|
|
3692
|
+
if (!this.container || !this.checkoutData) return;
|
|
3693
|
+
const theme = this.getComputedTheme();
|
|
3694
|
+
this.container.innerHTML = `
|
|
3695
|
+
<div class="zendfi-embedded-checkout" style="${this.getCheckoutContainerStyles(theme)}">
|
|
3696
|
+
${this.renderHeader()}
|
|
3697
|
+
${this.renderPaymentInfo()}
|
|
3698
|
+
${this.renderPaymentMethods()}
|
|
3699
|
+
${this.renderFooter()}
|
|
3700
|
+
</div>
|
|
3701
|
+
`;
|
|
3702
|
+
this.attachEventListeners();
|
|
3703
|
+
this.injectStyles();
|
|
3704
|
+
}
|
|
3705
|
+
/**
|
|
3706
|
+
* Render header section
|
|
3707
|
+
*/
|
|
3708
|
+
renderHeader() {
|
|
3709
|
+
if (!this.checkoutData) return "";
|
|
3710
|
+
return `
|
|
3711
|
+
<div class="zendfi-checkout-header" style="padding: 24px; border-bottom: 1px solid #e5e7eb;">
|
|
3712
|
+
<h2 style="margin: 0; font-size: 24px; font-weight: 600; color: ${this.config.theme.textColor || "#1f2937"};">
|
|
3713
|
+
Pay ${this.checkoutData.merchant_name}
|
|
3714
|
+
</h2>
|
|
3715
|
+
${this.checkoutData.description ? `
|
|
3716
|
+
<p style="margin: 8px 0 0 0; color: #6b7280; font-size: 14px;">
|
|
3717
|
+
${this.checkoutData.description}
|
|
3718
|
+
</p>
|
|
3719
|
+
` : ""}
|
|
3720
|
+
</div>
|
|
3721
|
+
`;
|
|
3722
|
+
}
|
|
3723
|
+
/**
|
|
3724
|
+
* Render payment information
|
|
3725
|
+
*/
|
|
3726
|
+
renderPaymentInfo() {
|
|
3727
|
+
if (!this.checkoutData) return "";
|
|
3728
|
+
return `
|
|
3729
|
+
<div class="zendfi-payment-info" style="padding: 24px; background: #f9fafb;">
|
|
3730
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
|
|
3731
|
+
<span style="font-size: 14px; color: #6b7280;">Amount</span>
|
|
3732
|
+
<span style="font-size: 24px; font-weight: 700; color: #1f2937;">
|
|
3733
|
+
$${this.checkoutData.amount_usd.toFixed(2)} ${this.checkoutData.token}
|
|
3734
|
+
</span>
|
|
3735
|
+
</div>
|
|
3736
|
+
|
|
3737
|
+
${this.checkoutData.allow_custom_amount ? this.renderCustomAmountInput() : ""}
|
|
3738
|
+
|
|
3739
|
+
<div style="display: flex; justify-content: space-between; align-items: center; padding: 12px; background: white; border-radius: 8px; margin-top: 12px;">
|
|
3740
|
+
<span style="font-size: 12px; color: #6b7280;">Network</span>
|
|
3741
|
+
<span style="font-size: 12px; font-weight: 600; color: #1f2937; text-transform: uppercase;">
|
|
3742
|
+
${this.checkoutData.solana_network}
|
|
3743
|
+
</span>
|
|
3744
|
+
</div>
|
|
3745
|
+
</div>
|
|
3746
|
+
`;
|
|
3747
|
+
}
|
|
3748
|
+
/**
|
|
3749
|
+
* Render custom amount input (Pay What You Want)
|
|
3750
|
+
*/
|
|
3751
|
+
renderCustomAmountInput() {
|
|
3752
|
+
if (!this.checkoutData) return "";
|
|
3753
|
+
return `
|
|
3754
|
+
<div style="margin-top: 16px;">
|
|
3755
|
+
<label style="display: block; font-size: 14px; color: #6b7280; margin-bottom: 8px;">
|
|
3756
|
+
Custom Amount (Optional)
|
|
3757
|
+
</label>
|
|
3758
|
+
<input
|
|
3759
|
+
type="number"
|
|
3760
|
+
id="zendfi-custom-amount"
|
|
3761
|
+
placeholder="${this.checkoutData.suggested_amount || this.checkoutData.amount_usd}"
|
|
3762
|
+
min="${this.checkoutData.minimum_amount || 0}"
|
|
3763
|
+
${this.checkoutData.maximum_amount ? `max="${this.checkoutData.maximum_amount}"` : ""}
|
|
3764
|
+
step="0.01"
|
|
3765
|
+
style="width: 100%; padding: 12px; border: 1px solid #d1d5db; border-radius: 8px; font-size: 16px;"
|
|
3766
|
+
/>
|
|
3767
|
+
${this.checkoutData.minimum_amount ? `
|
|
3768
|
+
<p style="font-size: 12px; color: #6b7280; margin-top: 4px;">
|
|
3769
|
+
Minimum: $${this.checkoutData.minimum_amount}
|
|
3770
|
+
</p>
|
|
3771
|
+
` : ""}
|
|
3772
|
+
</div>
|
|
3773
|
+
`;
|
|
3774
|
+
}
|
|
3775
|
+
/**
|
|
3776
|
+
* Render payment methods
|
|
3777
|
+
*/
|
|
3778
|
+
renderPaymentMethods() {
|
|
3779
|
+
if (!this.checkoutData) return "";
|
|
3780
|
+
return `
|
|
3781
|
+
<div class="zendfi-payment-methods" style="padding: 24px;">
|
|
3782
|
+
<h3 style="margin: 0 0 16px 0; font-size: 16px; font-weight: 600; color: #1f2937;">
|
|
3783
|
+
Payment Methods
|
|
3784
|
+
</h3>
|
|
3785
|
+
|
|
3786
|
+
${this.config.paymentMethods.qrCode ? this.renderQRCodeMethod() : ""}
|
|
3787
|
+
${this.config.paymentMethods.solanaWallet ? this.renderWalletMethod() : ""}
|
|
3788
|
+
${this.config.paymentMethods.walletConnect ? this.renderWalletConnectMethod() : ""}
|
|
3789
|
+
</div>
|
|
3790
|
+
`;
|
|
3791
|
+
}
|
|
3792
|
+
/**
|
|
3793
|
+
* Render QR code payment method
|
|
3794
|
+
*/
|
|
3795
|
+
renderQRCodeMethod() {
|
|
3796
|
+
if (!this.checkoutData) return "";
|
|
3797
|
+
return `
|
|
3798
|
+
<div class="zendfi-payment-method" style="padding: 16px; border: 1px solid #e5e7eb; border-radius: 12px; margin-bottom: 16px;">
|
|
3799
|
+
<div style="text-align: center;">
|
|
3800
|
+
<p style="font-size: 14px; color: #6b7280; margin-bottom: 16px;">
|
|
3801
|
+
Scan with Solana wallet
|
|
3802
|
+
</p>
|
|
3803
|
+
<div id="zendfi-qr-container" style="display: flex; justify-content: center; margin-bottom: 16px;">
|
|
3804
|
+
<canvas id="zendfi-qr-code"></canvas>
|
|
3805
|
+
</div>
|
|
3806
|
+
<button
|
|
3807
|
+
id="zendfi-copy-address"
|
|
3808
|
+
style="padding: 10px 20px; background: #f3f4f6; border: 1px solid #d1d5db; border-radius: 8px; cursor: pointer; font-size: 14px; color: #374151; transition: all 0.2s;"
|
|
3809
|
+
onmouseover="this.style.background='#e5e7eb'"
|
|
3810
|
+
onmouseout="this.style.background='#f3f4f6'"
|
|
3811
|
+
>
|
|
3812
|
+
\u{1F4CB} Copy Address
|
|
3813
|
+
</button>
|
|
3814
|
+
</div>
|
|
3815
|
+
</div>
|
|
3816
|
+
`;
|
|
3817
|
+
}
|
|
3818
|
+
/**
|
|
3819
|
+
* Render browser wallet method
|
|
3820
|
+
*/
|
|
3821
|
+
renderWalletMethod() {
|
|
3822
|
+
return `
|
|
3823
|
+
<div class="zendfi-payment-method" style="margin-bottom: 16px;">
|
|
3824
|
+
<button
|
|
3825
|
+
id="zendfi-connect-wallet"
|
|
3826
|
+
style="width: 100%; padding: 16px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 12px; font-size: 16px; font-weight: 600; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);"
|
|
3827
|
+
onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 12px rgba(0, 0, 0, 0.15)';"
|
|
3828
|
+
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 4px 6px rgba(0, 0, 0, 0.1)';"
|
|
3829
|
+
>
|
|
3830
|
+
\u{1F517} Connect Wallet
|
|
3831
|
+
</button>
|
|
3832
|
+
</div>
|
|
3833
|
+
`;
|
|
3834
|
+
}
|
|
3835
|
+
/**
|
|
3836
|
+
* Render WalletConnect method
|
|
3837
|
+
*/
|
|
3838
|
+
renderWalletConnectMethod() {
|
|
3839
|
+
return `
|
|
3840
|
+
<div class="zendfi-payment-method">
|
|
3841
|
+
<button
|
|
3842
|
+
id="zendfi-wallet-connect"
|
|
3843
|
+
style="width: 100%; padding: 16px; background: white; color: #1f2937; border: 2px solid #e5e7eb; border-radius: 12px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.2s;"
|
|
3844
|
+
onmouseover="this.style.borderColor='#667eea'; this.style.background='#f9fafb';"
|
|
3845
|
+
onmouseout="this.style.borderColor='#e5e7eb'; this.style.background='white';"
|
|
3846
|
+
>
|
|
3847
|
+
\u{1F4F1} WalletConnect
|
|
3848
|
+
</button>
|
|
3849
|
+
</div>
|
|
3850
|
+
`;
|
|
3851
|
+
}
|
|
3852
|
+
/**
|
|
3853
|
+
* Render footer
|
|
3854
|
+
*/
|
|
3855
|
+
renderFooter() {
|
|
3856
|
+
return `
|
|
3857
|
+
<div class="zendfi-checkout-footer" style="padding: 16px 24px; border-top: 1px solid #e5e7eb; text-align: center;">
|
|
3858
|
+
<p style="margin: 0; font-size: 12px; color: #9ca3af;">
|
|
3859
|
+
Powered by <a href="https://zendfi.tech" target="_blank" style="color: #667eea; text-decoration: none; font-weight: 600;">ZendFi</a>
|
|
3860
|
+
</p>
|
|
3861
|
+
</div>
|
|
3862
|
+
`;
|
|
3863
|
+
}
|
|
3864
|
+
/**
|
|
3865
|
+
* Render success state
|
|
3866
|
+
*/
|
|
3867
|
+
renderSuccess() {
|
|
3868
|
+
if (!this.container) return;
|
|
3869
|
+
this.container.innerHTML = `
|
|
3870
|
+
<div class="zendfi-checkout-success" style="${this.getSuccessStyles()}">
|
|
3871
|
+
<div style="font-size: 64px; margin-bottom: 16px;">\u2705</div>
|
|
3872
|
+
<h3 style="margin: 0 0 8px 0; font-size: 24px; font-weight: 600; color: #059669;">
|
|
3873
|
+
Payment Successful!
|
|
3874
|
+
</h3>
|
|
3875
|
+
<p style="margin: 0; color: #6b7280; font-size: 14px;">
|
|
3876
|
+
Your payment has been confirmed on the blockchain.
|
|
3877
|
+
</p>
|
|
3878
|
+
</div>
|
|
3879
|
+
`;
|
|
3880
|
+
}
|
|
3881
|
+
/**
|
|
3882
|
+
* Render error state
|
|
3883
|
+
*/
|
|
3884
|
+
renderError(message) {
|
|
3885
|
+
if (!this.container) return;
|
|
3886
|
+
this.container.innerHTML = `
|
|
3887
|
+
<div class="zendfi-checkout-error" style="${this.getErrorStyles()}">
|
|
3888
|
+
<div style="font-size: 64px; margin-bottom: 16px;">\u274C</div>
|
|
3889
|
+
<h3 style="margin: 0 0 8px 0; font-size: 24px; font-weight: 600; color: #dc2626;">
|
|
3890
|
+
Payment Failed
|
|
3891
|
+
</h3>
|
|
3892
|
+
<p style="margin: 0; color: #6b7280; font-size: 14px;">
|
|
3893
|
+
${message}
|
|
3894
|
+
</p>
|
|
3895
|
+
</div>
|
|
3896
|
+
`;
|
|
3897
|
+
}
|
|
3898
|
+
/**
|
|
3899
|
+
* Attach event listeners to interactive elements
|
|
3900
|
+
*/
|
|
3901
|
+
attachEventListeners() {
|
|
3902
|
+
const qrCanvas = document.getElementById("zendfi-qr-code");
|
|
3903
|
+
if (qrCanvas && this.checkoutData) {
|
|
3904
|
+
this.generateQRCode(qrCanvas, this.checkoutData.qr_code);
|
|
3905
|
+
}
|
|
3906
|
+
const copyBtn = document.getElementById("zendfi-copy-address");
|
|
3907
|
+
if (copyBtn && this.checkoutData) {
|
|
3908
|
+
copyBtn.addEventListener("click", () => {
|
|
3909
|
+
navigator.clipboard.writeText(this.checkoutData.wallet_address);
|
|
3910
|
+
copyBtn.textContent = "\u2713 Copied!";
|
|
3911
|
+
setTimeout(() => {
|
|
3912
|
+
copyBtn.textContent = "\u{1F4CB} Copy Address";
|
|
3913
|
+
}, 2e3);
|
|
3914
|
+
});
|
|
3915
|
+
}
|
|
3916
|
+
const connectBtn = document.getElementById("zendfi-connect-wallet");
|
|
3917
|
+
if (connectBtn) {
|
|
3918
|
+
connectBtn.addEventListener("click", () => this.handleWalletConnect());
|
|
3919
|
+
}
|
|
3920
|
+
const walletConnectBtn = document.getElementById("zendfi-wallet-connect");
|
|
3921
|
+
if (walletConnectBtn) {
|
|
3922
|
+
walletConnectBtn.addEventListener("click", () => this.handleWalletConnectScan());
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
/**
|
|
3926
|
+
* Handle wallet connection and payment
|
|
3927
|
+
*/
|
|
3928
|
+
async handleWalletConnect() {
|
|
3929
|
+
try {
|
|
3930
|
+
const provider = window.solana;
|
|
3931
|
+
if (!provider) {
|
|
3932
|
+
alert("No Solana wallet found. Please install Phantom, Solflare, or another Solana wallet.");
|
|
3933
|
+
return;
|
|
3934
|
+
}
|
|
3935
|
+
const connectBtn = document.getElementById("zendfi-connect-wallet");
|
|
3936
|
+
if (connectBtn) {
|
|
3937
|
+
connectBtn.textContent = "\u{1F504} Connecting...";
|
|
3938
|
+
connectBtn.disabled = true;
|
|
3939
|
+
}
|
|
3940
|
+
await provider.connect();
|
|
3941
|
+
const publicKey = provider.publicKey.toString();
|
|
3942
|
+
if (connectBtn) {
|
|
3943
|
+
connectBtn.textContent = "\u{1F528} Building transaction...";
|
|
3944
|
+
}
|
|
3945
|
+
const response = await fetch(
|
|
3946
|
+
`${this.config.apiUrl}/api/v1/payments/${this.checkoutData.payment_id}/build-transaction`,
|
|
3947
|
+
{
|
|
3948
|
+
method: "POST",
|
|
3949
|
+
headers: { "Content-Type": "application/json" },
|
|
3950
|
+
body: JSON.stringify({
|
|
3951
|
+
payer_wallet: publicKey,
|
|
3952
|
+
prefer_gasless: true
|
|
3953
|
+
})
|
|
3954
|
+
}
|
|
3955
|
+
);
|
|
3956
|
+
if (!response.ok) {
|
|
3957
|
+
throw new Error("Failed to build transaction");
|
|
3958
|
+
}
|
|
3959
|
+
const { transaction: transactionBase64, is_gasless } = await response.json();
|
|
3960
|
+
if (connectBtn) {
|
|
3961
|
+
connectBtn.textContent = "\u270D\uFE0F Sign transaction...";
|
|
3962
|
+
}
|
|
3963
|
+
const solanaWeb3 = window.solanaWeb3;
|
|
3964
|
+
const transactionBuffer = Uint8Array.from(atob(transactionBase64), (c) => c.charCodeAt(0));
|
|
3965
|
+
const transaction = solanaWeb3.Transaction.from(transactionBuffer);
|
|
3966
|
+
const signedTransaction = await provider.signTransaction(transaction);
|
|
3967
|
+
if (connectBtn) {
|
|
3968
|
+
connectBtn.textContent = "\u{1F4E1} Submitting...";
|
|
3969
|
+
}
|
|
3970
|
+
if (is_gasless) {
|
|
3971
|
+
const serialized = signedTransaction.serialize();
|
|
3972
|
+
const signedBase64 = btoa(String.fromCharCode(...serialized));
|
|
3973
|
+
const submitResponse = await fetch(
|
|
3974
|
+
`${this.config.apiUrl}/api/v1/payments/${this.checkoutData.payment_id}/submit-gasless-transaction`,
|
|
3975
|
+
{
|
|
3976
|
+
method: "POST",
|
|
3977
|
+
headers: { "Content-Type": "application/json" },
|
|
3978
|
+
body: JSON.stringify({
|
|
3979
|
+
signed_transaction: signedBase64
|
|
3980
|
+
})
|
|
3981
|
+
}
|
|
3982
|
+
);
|
|
3983
|
+
if (!submitResponse.ok) {
|
|
3984
|
+
const errorData = await submitResponse.json().catch(() => ({}));
|
|
3985
|
+
throw new Error(errorData.error || "Failed to submit gasless transaction");
|
|
3986
|
+
}
|
|
3987
|
+
} else {
|
|
3988
|
+
const signature = signedTransaction.signature?.toString();
|
|
3989
|
+
if (!signature) {
|
|
3990
|
+
throw new Error("Transaction signature missing");
|
|
3991
|
+
}
|
|
3992
|
+
const submitResponse = await fetch(
|
|
3993
|
+
`${this.config.apiUrl}/api/v1/payments/${this.checkoutData.payment_id}/submit-transaction`,
|
|
3994
|
+
{
|
|
3995
|
+
method: "POST",
|
|
3996
|
+
headers: { "Content-Type": "application/json" },
|
|
3997
|
+
body: JSON.stringify({
|
|
3998
|
+
transaction_signature: signature
|
|
3999
|
+
})
|
|
4000
|
+
}
|
|
4001
|
+
);
|
|
4002
|
+
if (!submitResponse.ok) {
|
|
4003
|
+
const errorData = await submitResponse.json().catch(() => ({}));
|
|
4004
|
+
throw new Error(errorData.error || "Failed to submit transaction");
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
if (connectBtn) {
|
|
4008
|
+
connectBtn.textContent = "\u23F3 Confirming...";
|
|
4009
|
+
}
|
|
4010
|
+
} catch (error) {
|
|
4011
|
+
console.error("Wallet connection error:", error);
|
|
4012
|
+
const connectBtn = document.getElementById("zendfi-connect-wallet");
|
|
4013
|
+
if (connectBtn) {
|
|
4014
|
+
connectBtn.textContent = "\u{1F517} Connect Wallet";
|
|
4015
|
+
connectBtn.disabled = false;
|
|
4016
|
+
}
|
|
4017
|
+
this.config.onError({
|
|
4018
|
+
code: "WALLET_ERROR",
|
|
4019
|
+
message: error instanceof Error ? error.message : "Wallet connection failed",
|
|
4020
|
+
details: error
|
|
4021
|
+
});
|
|
4022
|
+
alert(`Payment failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
/**
|
|
4026
|
+
* Handle mobile wallet connection via Solana Pay deep link
|
|
4027
|
+
*/
|
|
4028
|
+
handleWalletConnectScan() {
|
|
4029
|
+
if (!this.checkoutData) return;
|
|
4030
|
+
const solanaPayUrl = `solana:${this.checkoutData.wallet_address}?amount=${this.checkoutData.amount_usd}&spl-token=${this.checkoutData.token}&reference=${this.checkoutData.payment_id}&label=${encodeURIComponent(this.checkoutData.merchant_name)}&message=${encodeURIComponent(this.checkoutData.description || "Payment")}`;
|
|
4031
|
+
if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
|
|
4032
|
+
window.location.href = solanaPayUrl;
|
|
4033
|
+
} else {
|
|
4034
|
+
navigator.clipboard.writeText(solanaPayUrl).then(() => {
|
|
4035
|
+
alert("Solana Pay link copied! Open it on your mobile wallet or scan the QR code above.");
|
|
4036
|
+
}).catch(() => {
|
|
4037
|
+
alert("Solana Pay URL: " + solanaPayUrl);
|
|
4038
|
+
});
|
|
4039
|
+
}
|
|
4040
|
+
}
|
|
4041
|
+
/**
|
|
4042
|
+
* Generate QR code on canvas using QRious library
|
|
4043
|
+
*/
|
|
4044
|
+
generateQRCode(canvas, data) {
|
|
4045
|
+
try {
|
|
4046
|
+
const QRious = window.QRious;
|
|
4047
|
+
if (!QRious) {
|
|
4048
|
+
console.error("QRious library not loaded");
|
|
4049
|
+
return;
|
|
4050
|
+
}
|
|
4051
|
+
new QRious({
|
|
4052
|
+
element: canvas,
|
|
4053
|
+
value: data,
|
|
4054
|
+
size: 256,
|
|
4055
|
+
level: "M"
|
|
4056
|
+
});
|
|
4057
|
+
} catch (error) {
|
|
4058
|
+
console.error("Failed to generate QR code:", error);
|
|
4059
|
+
}
|
|
4060
|
+
}
|
|
4061
|
+
/**
|
|
4062
|
+
* Inject custom styles
|
|
4063
|
+
*/
|
|
4064
|
+
injectStyles() {
|
|
4065
|
+
const styleId = "zendfi-embedded-styles";
|
|
4066
|
+
if (document.getElementById(styleId)) return;
|
|
4067
|
+
const style = document.createElement("style");
|
|
4068
|
+
style.id = styleId;
|
|
4069
|
+
style.textContent = `
|
|
4070
|
+
.zendfi-embedded-checkout * {
|
|
4071
|
+
box-sizing: border-box;
|
|
4072
|
+
}
|
|
4073
|
+
|
|
4074
|
+
@keyframes zendfi-spin {
|
|
4075
|
+
to { transform: rotate(360deg); }
|
|
4076
|
+
}
|
|
4077
|
+
`;
|
|
4078
|
+
document.head.appendChild(style);
|
|
4079
|
+
}
|
|
4080
|
+
/**
|
|
4081
|
+
* Load external dependencies (QR code library, Solana web3.js)
|
|
4082
|
+
*/
|
|
4083
|
+
async loadDependencies() {
|
|
4084
|
+
if (!window.QRious) {
|
|
4085
|
+
await this.loadScript("https://cdn.jsdelivr.net/npm/qrious@4/dist/qrious.min.js");
|
|
4086
|
+
}
|
|
4087
|
+
if (!window.solanaWeb3) {
|
|
4088
|
+
await this.loadScript("https://unpkg.com/@solana/web3.js@1.95.2/lib/index.iife.min.js");
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
/**
|
|
4092
|
+
* Load external script
|
|
4093
|
+
*/
|
|
4094
|
+
loadScript(src) {
|
|
4095
|
+
return new Promise((resolve, reject) => {
|
|
4096
|
+
if (document.querySelector(`script[src="${src}"]`)) {
|
|
4097
|
+
resolve();
|
|
4098
|
+
return;
|
|
4099
|
+
}
|
|
4100
|
+
const script = document.createElement("script");
|
|
4101
|
+
script.src = src;
|
|
4102
|
+
script.onload = () => resolve();
|
|
4103
|
+
script.onerror = () => reject(new Error(`Failed to load ${src}`));
|
|
4104
|
+
document.head.appendChild(script);
|
|
4105
|
+
});
|
|
4106
|
+
}
|
|
4107
|
+
/**
|
|
4108
|
+
* Get computed theme with defaults
|
|
4109
|
+
*/
|
|
4110
|
+
getComputedTheme() {
|
|
4111
|
+
return {
|
|
4112
|
+
primaryColor: this.config.theme.primaryColor || "#667eea",
|
|
4113
|
+
backgroundColor: this.config.theme.backgroundColor || "#ffffff",
|
|
4114
|
+
borderRadius: this.config.theme.borderRadius || "16px",
|
|
4115
|
+
fontFamily: this.config.theme.fontFamily || '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
4116
|
+
textColor: this.config.theme.textColor || "#1f2937",
|
|
4117
|
+
buttonStyle: this.config.theme.buttonStyle || "solid"
|
|
4118
|
+
};
|
|
4119
|
+
}
|
|
4120
|
+
/**
|
|
4121
|
+
* Style helpers
|
|
4122
|
+
*/
|
|
4123
|
+
getCheckoutContainerStyles(theme) {
|
|
4124
|
+
return `
|
|
4125
|
+
font-family: ${theme.fontFamily};
|
|
4126
|
+
background: ${theme.backgroundColor};
|
|
4127
|
+
border-radius: ${theme.borderRadius};
|
|
4128
|
+
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
|
4129
|
+
overflow: hidden;
|
|
4130
|
+
max-width: 500px;
|
|
4131
|
+
margin: 0 auto;
|
|
4132
|
+
`.replace(/\s+/g, " ").trim();
|
|
4133
|
+
}
|
|
4134
|
+
getLoadingStyles() {
|
|
4135
|
+
return `
|
|
4136
|
+
display: flex;
|
|
4137
|
+
flex-direction: column;
|
|
4138
|
+
align-items: center;
|
|
4139
|
+
justify-content: center;
|
|
4140
|
+
padding: 80px 24px;
|
|
4141
|
+
text-align: center;
|
|
4142
|
+
`.replace(/\s+/g, " ").trim();
|
|
4143
|
+
}
|
|
4144
|
+
getSpinnerStyles() {
|
|
4145
|
+
return `
|
|
4146
|
+
width: 40px;
|
|
4147
|
+
height: 40px;
|
|
4148
|
+
border: 4px solid #f3f4f6;
|
|
4149
|
+
border-top-color: #667eea;
|
|
4150
|
+
border-radius: 50%;
|
|
4151
|
+
animation: zendfi-spin 0.8s linear infinite;
|
|
4152
|
+
`.replace(/\s+/g, " ").trim();
|
|
4153
|
+
}
|
|
4154
|
+
getSuccessStyles() {
|
|
4155
|
+
return this.getLoadingStyles();
|
|
4156
|
+
}
|
|
4157
|
+
getErrorStyles() {
|
|
4158
|
+
return this.getLoadingStyles();
|
|
4159
|
+
}
|
|
4160
|
+
};
|
|
4161
|
+
if (typeof window !== "undefined") {
|
|
4162
|
+
window.ZendFiEmbeddedCheckout = ZendFiEmbeddedCheckout;
|
|
4163
|
+
}
|
|
4164
|
+
|
|
3493
4165
|
// src/device-bound-session-keys.ts
|
|
3494
4166
|
init_device_bound_crypto();
|
|
3495
4167
|
var import_web32 = require("@solana/web3.js");
|
|
@@ -6267,6 +6939,7 @@ var PerformanceMonitor = class {
|
|
|
6267
6939
|
WalletConnector,
|
|
6268
6940
|
WebhookError,
|
|
6269
6941
|
ZendFiClient,
|
|
6942
|
+
ZendFiEmbeddedCheckout,
|
|
6270
6943
|
ZendFiError,
|
|
6271
6944
|
ZendFiSessionKeyManager,
|
|
6272
6945
|
asAgentKeyId,
|