@paypercut/checkout-js 1.0.11 → 1.0.13

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 CHANGED
@@ -17,6 +17,8 @@ A lightweight, framework-agnostic JavaScript SDK for embedding Paypercut Checkou
17
17
  - [Quick Start](#quick-start)
18
18
  - [API Reference](#api-reference)
19
19
  - [Events](#events)
20
+ - [Form Validation for Wallet Payments](#form-validation-for-wallet-payments)
21
+ - [Types](#types)
20
22
  - [Usage Examples](#usage-examples)
21
23
  - [Vanilla JavaScript](#vanilla-javascript-cdn)
22
24
  - [TypeScript / ESM](#typescript--esm)
@@ -54,12 +56,12 @@ pnpm add @paypercut/checkout-js
54
56
 
55
57
  ```html
56
58
  <!-- jsDelivr (recommended with SRI) -->
57
- <script src="https://cdn.jsdelivr.net/npm/@paypercut/checkout-js@1.0.11/dist/paypercut-checkout.iife.min.js"
59
+ <script src="https://cdn.jsdelivr.net/npm/@paypercut/checkout-js@1.0.13/dist/paypercut-checkout.iife.min.js"
58
60
  integrity="sha384-..."
59
61
  crossorigin="anonymous"></script>
60
62
 
61
63
  <!-- or UNPKG -->
62
- <script src="https://unpkg.com/@paypercut/checkout-js@1.0.11/dist/paypercut-checkout.iife.min.js"></script>
64
+ <script src="https://unpkg.com/@paypercut/checkout-js@1.0.13/dist/paypercut-checkout.iife.min.js"></script>
63
65
  ```
64
66
 
65
67
  ---
@@ -118,12 +120,19 @@ Creates a new checkout instance.
118
120
 
119
121
  #### Options
120
122
 
121
- | Option | Type | Required | Description |
122
- |--------|------|----------|-------------|
123
- | `id` | `string` | Yes | Checkout session identifier |
124
- | `containerId` | `string \| HTMLElement` | Yes | CSS selector or element where iframe mounts. |
123
+ | Option | Type | Required | Default | Description |
124
+ |------------------|------|----------|---------|-------------|
125
+ | `id` | `string` | Yes | — | Checkout session identifier (e.g., `CHK_12345`) |
126
+ | `containerId` | `string \| HTMLElement` | Yes | — | CSS selector or element where iframe mounts |
127
+ | `locale` | `string` | No | `'auto'` | Locale for checkout UI. Options: `'auto'`, `'en'`, `'en-GB'`, `'bg'`, `'bg-BG'` |
128
+ | `lang` | `string` | No | `'auto'` | Locale for checkout UI. Options: `'auto'`, `'en'`, `'en-GB'`, `'bg'`, `'bg-BG'` |
129
+ | `ui_mode` | `'hosted' \| 'embedded'` | No | `'embedded'` | UI mode for checkout display |
130
+ | `wallet_options` | `string[]` | No | `['apple_pay', 'google_pay']` | Digital wallet options. Pass `[]` to disable all wallets |
131
+ | `form_only` | `boolean` | No | `false` | Show only payment form (no Pay Now button - use external button with `submit()`) |
125
132
 
126
- #### Example
133
+ #### Examples
134
+
135
+ **Basic initialization:**
127
136
 
128
137
  ```typescript
129
138
  const checkout = PaypercutCheckout({
@@ -132,6 +141,55 @@ const checkout = PaypercutCheckout({
132
141
  });
133
142
  ```
134
143
 
144
+ **With all options:**
145
+
146
+ ```typescript
147
+ const checkout = PaypercutCheckout({
148
+ id: 'CHK_12345',
149
+ containerId: '#checkout-container',
150
+ locale: 'en', // 'auto' | 'en' | 'en-GB' | 'bg' | 'bg-BG'
151
+ lang: 'en', // 'auto' | 'en' | 'en-GB' | 'bg' | 'bg-BG'
152
+ ui_mode: 'embedded', // 'hosted' | 'embedded'
153
+ wallet_options: ['apple_pay', 'google_pay'], // Can be empty array [] or contain one/both options
154
+ form_only: false // Set true to hide Pay Now button (use external button)
155
+ });
156
+ ```
157
+
158
+ **Disable wallet payments:**
159
+
160
+ ```typescript
161
+ const checkout = PaypercutCheckout({
162
+ id: 'CHK_12345',
163
+ containerId: '#checkout-container',
164
+ wallet_options: [] // No Apple Pay or Google Pay buttons
165
+ });
166
+ ```
167
+
168
+ **Only Apple Pay:**
169
+
170
+ ```typescript
171
+ const checkout = PaypercutCheckout({
172
+ id: 'CHK_12345',
173
+ containerId: '#checkout-container',
174
+ wallet_options: ['apple_pay'] // Only Apple Pay, no Google Pay
175
+ });
176
+ ```
177
+
178
+ **Form-only mode (external submit button):**
179
+
180
+ ```typescript
181
+ const checkout = PaypercutCheckout({
182
+ id: 'CHK_12345',
183
+ containerId: '#checkout-container',
184
+ form_only: true // No Pay Now button inside checkout
185
+ });
186
+
187
+ // Use your own button to trigger payment
188
+ document.getElementById('my-pay-button').addEventListener('click', () => {
189
+ checkout.submit();
190
+ });
191
+ ```
192
+
135
193
  ### Instance Methods
136
194
 
137
195
  #### `render()`
@@ -197,39 +255,125 @@ if (checkout.isMounted()) {
197
255
 
198
256
  ## Events
199
257
 
200
- Subscribe to events using the `on()` method:
258
+ Subscribe to events using the `on()` method. You can use string event names or the `SdkEvent` enum.
259
+
260
+ ### Event Reference
261
+
262
+ | Event | Enum | Description | Payload |
263
+ |-------|------|-------------|---------|
264
+ | `loaded` | `SdkEvent.Loaded` | Checkout iframe has finished loading | `void` |
265
+ | `success` | `SdkEvent.Success` | Payment completed successfully | `PaymentSuccessPayload` |
266
+ | `error` | `SdkEvent.Error` | Terminal failure (tokenize, confirm, or 3DS) | `ApiErrorPayload` |
267
+ | `expired` | `SdkEvent.Expired` | Checkout session expired | `void` |
268
+ | `threeds_started` | `SdkEvent.ThreeDSStarted` | 3DS challenge flow started | `object` |
269
+ | `threeds_complete` | `SdkEvent.ThreeDSComplete` | 3DS challenge completed | `object` |
270
+ | `threeds_canceled` | `SdkEvent.ThreeDSCanceled` | 3DS challenge canceled by user | `object` |
271
+ | `threeds_error` | `SdkEvent.ThreeDSError` | 3DS challenge error | `ApiErrorPayload` or `object` |
272
+
273
+ ### Usage Examples
274
+
275
+ **Using string event names:**
276
+
277
+ ```ts
278
+ const checkout = PaypercutCheckout({ id: 'CHK_12345', containerId: '#checkout' });
279
+
280
+ checkout.on('loaded', () => {
281
+ console.log('Checkout loaded');
282
+ });
283
+
284
+ checkout.on('success', (payload) => {
285
+ // PaymentSuccessPayload
286
+ console.log('Payment successful');
287
+ console.log('Card brand:', payload.payment_method.brand);
288
+ console.log('Last 4:', payload.payment_method.last4);
289
+ console.log('Expiry:', payload.payment_method.exp_month + '/' + payload.payment_method.exp_year);
290
+ });
291
+
292
+ checkout.on('error', (err) => {
293
+ console.error('Payment error:', err.code, err.message);
294
+ });
295
+
296
+ checkout.on('expired', () => {
297
+ console.warn('Checkout session expired');
298
+ });
299
+ ```
300
+
301
+ **Using SdkEvent enum (recommended for TypeScript):**
302
+
303
+ ```ts
304
+ import { PaypercutCheckout, SdkEvent } from '@paypercut/checkout-js';
305
+
306
+ const checkout = PaypercutCheckout({ id: 'CHK_12345', containerId: '#checkout' });
307
+
308
+ checkout.on(SdkEvent.Loaded, () => {
309
+ console.log('Checkout loaded');
310
+ });
311
+
312
+ checkout.on(SdkEvent.Success, (payload) => {
313
+ console.log('Payment successful', payload.payment_method);
314
+ });
315
+
316
+ checkout.on(SdkEvent.Error, (err) => {
317
+ console.error('Payment error:', err.code, err.message);
318
+ });
319
+
320
+ checkout.on(SdkEvent.Expired, () => {
321
+ console.warn('Checkout session expired');
322
+ });
323
+
324
+ // 3DS events
325
+ checkout.on(SdkEvent.ThreeDSStarted, (ctx) => {
326
+ console.log('3DS challenge started');
327
+ });
201
328
 
202
- | Event | Description | Payload |
203
- |-------|-------------|---------|
204
- | `loaded` | Checkout iframe has finished loading | `void` |
205
- | `success` | Payment completed successfully | `PaymentSuccessPayload` |
206
- | `error` | Terminal failure (tokenize, confirm, or 3DS) | `ApiErrorPayload` (see details below) |
207
- | `expired` | Checkout session expired | `void` |
208
- | `threeds_started` | 3DS challenge flow started; SDK modal opened | `object` (flow context) |
209
- | `threeds_complete` | 3DS challenge completed | `object` (auth result context) |
210
- | `threeds_canceled` | 3DS challenge canceled by user | `object` (flow context) |
211
- | `threeds_error` | 3DS challenge error | `ApiErrorPayload` (if provided) or `object` |
329
+ checkout.on(SdkEvent.ThreeDSComplete, (payload) => {
330
+ console.log('3DS completed');
331
+ });
212
332
 
333
+ checkout.on(SdkEvent.ThreeDSCanceled, (payload) => {
334
+ console.log('3DS canceled by user');
335
+ });
336
+
337
+ checkout.on(SdkEvent.ThreeDSError, (err) => {
338
+ console.error('3DS error:', err);
339
+ });
340
+ ```
213
341
 
214
- #### Success payload example
342
+ ### Success Payload
343
+
344
+ The `success` event returns a `PaymentSuccessPayload` with the payment method details:
345
+
346
+ ```ts
347
+ type PaymentSuccessPayload = {
348
+ payment_method: {
349
+ brand: string; // e.g., 'visa', 'mastercard', 'amex'
350
+ last4: string; // Last 4 digits of card number
351
+ exp_month: number; // Expiration month (1-12)
352
+ exp_year: number; // Expiration year (e.g., 2030)
353
+ };
354
+ };
355
+ ```
356
+
357
+ **Example:**
215
358
 
216
359
  ```ts
217
360
  checkout.on('success', (payload) => {
218
- // payload: PaymentSuccessPayload
219
- // Example:
361
+ // payload:
220
362
  // {
221
363
  // payment_method: {
222
364
  // brand: 'visa',
223
365
  // last4: '4242',
224
366
  // exp_month: 12,
225
- // exp_year: 2030,
367
+ // exp_year: 2030
226
368
  // }
227
369
  // }
228
- console.log('PM last4', payload.payment_method.last4);
370
+
371
+ console.log(`Paid with ${payload.payment_method.brand} ending in ${payload.payment_method.last4}`);
372
+ // Output: "Paid with visa ending in 4242"
229
373
  });
230
374
  ```
231
375
 
232
- #### Error payloads overview
376
+ ### Error Handling
233
377
 
234
378
  ```ts
235
379
  checkout.on('error', (err) => {
@@ -252,99 +396,185 @@ checkout.on('error', (err) => {
252
396
  });
253
397
  ```
254
398
 
255
- ### 3DS Events (enum usage recommended)
399
+ ### Notes
256
400
 
257
- You can subscribe using string event names or the SdkEvent enum:
401
+ - `error`: the SDK forwards a normalized `ApiErrorPayload` when provided by Hosted Checkout.
402
+ - `threeds_error`: forwards `payload.error` when available; otherwise the raw message data.
403
+ - `threeds_*` non-error events: payload is forwarded as-is from Hosted Checkout (shape may evolve).
258
404
 
259
- ```ts
260
- import { PaypercutCheckout, SdkEvent } from '@paypercut/checkout-js';
405
+ ---
261
406
 
262
- const checkout = PaypercutCheckout({ id: 'CHK_12345', containerId: '#checkout' });
407
+ ## Form Validation for Wallet Payments
263
408
 
264
- checkout.on(SdkEvent.ThreeDSStarted, (ctx) => {
265
- // e.g., show your own UI indicator or log ctx
266
- });
409
+ When using Apple Pay or Google Pay, you may need to validate your own form fields (e.g., email, shipping address) before the wallet payment sheet opens. The SDK provides a `form_validation` event and methods to control this flow.
267
410
 
268
- checkout.on(SdkEvent.ThreeDSComplete, (payload) => {
269
- // 3DS completed successfully; Hosted Checkout will continue the flow
270
- });
411
+ ### How It Works
271
412
 
272
- checkout.on(SdkEvent.ThreeDSCanceled, (payload) => {
273
- // User canceled the 3DS challenge
274
- });
413
+ 1. User clicks Apple Pay or Google Pay button in the checkout
414
+ 2. SDK emits `form_validation` event to your code
415
+ 3. You validate your form and call either:
416
+ - `completeFormValidation(wallet)` - allows wallet to proceed
417
+ - `failFormValidation(wallet, errors)` - blocks wallet, you show your own errors
418
+ 4. If validation passes, the wallet payment sheet opens
275
419
 
276
- checkout.on(SdkEvent.ThreeDSError, (err) => {
277
- // Normalized ApiErrorPayload when available; fallback may be a raw object
278
- console.error('3DS error:', err);
279
- });
280
- ```
420
+ ### Event: `form_validation`
281
421
 
282
- Payload notes:
283
- - `error`: the SDK forwards a normalized `ApiErrorPayload` when provided by Hosted Checkout.
284
- - `threeds_error`: forwards `payload.error` when available; otherwise the raw message data.
285
- - `threeds_*` non-error events: payload is forwarded as-is from Hosted Checkout (shape may evolve).
422
+ | Property | Type | Description |
423
+ |----------|------|-------------|
424
+ | `checkoutId` | `string` | The checkout session ID |
425
+ | `wallet` | `'apple_pay' \| 'google_pay'` | Which wallet button was clicked |
286
426
 
427
+ ### Methods
287
428
 
288
- ### Core events (loaded, success, error, expired)
429
+ #### `completeFormValidation(wallet)`
289
430
 
290
- Using enums:
431
+ Call this when your form is valid. The wallet payment sheet will open.
291
432
 
292
- ```ts
293
- import { PaypercutCheckout, SdkEvent } from '@paypercut/checkout-js';
433
+ ```typescript
434
+ checkout.completeFormValidation('google_pay');
435
+ ```
294
436
 
295
- const checkout = PaypercutCheckout({ id: 'CHK_12345', containerId: '#checkout' });
437
+ #### `failFormValidation(wallet, errors?)`
296
438
 
297
- checkout.on(SdkEvent.Loaded, () => {
298
- console.log('Checkout loaded');
299
- });
439
+ Call this when your form has errors. The wallet will not open, and you should display your own error messages.
300
440
 
301
- checkout.on(SdkEvent.Success, (payload) => {
302
- console.log('Payment successful', payload.payment_method);
303
- });
441
+ ```typescript
442
+ checkout.failFormValidation('apple_pay', [
443
+ { code: 'invalid_email', message: 'Please enter a valid email address' },
444
+ { code: 'missing_address', message: 'Shipping address is required' }
445
+ ]);
446
+ ```
304
447
 
305
- checkout.on(SdkEvent.Error, (err) => {
306
- // err: ApiErrorPayload
307
- console.error('Payment error:', err.code, err.message);
448
+ ### Basic Example
449
+
450
+ ```typescript
451
+ import { PaypercutCheckout, SdkEvent } from '@paypercut/checkout-js';
452
+
453
+ const checkout = PaypercutCheckout({
454
+ id: 'CHK_12345',
455
+ containerId: '#checkout',
456
+ wallet_options: ['apple_pay', 'google_pay']
308
457
  });
309
458
 
310
- checkout.on(SdkEvent.Expired, () => {
311
- console.warn('Checkout session expired');
459
+ // Handle form validation for wallet payments
460
+ checkout.on(SdkEvent.FormValidation, ({ wallet, checkoutId }) => {
461
+ // Validate your own form fields
462
+ const email = document.getElementById('email').value;
463
+ const isEmailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
464
+
465
+ if (isEmailValid) {
466
+ // Form is valid - allow wallet to proceed
467
+ checkout.completeFormValidation(wallet);
468
+ } else {
469
+ // Form has errors - block wallet and show your errors
470
+ document.getElementById('email-error').textContent = 'Please enter a valid email';
471
+ checkout.failFormValidation(wallet, [
472
+ { code: 'invalid_email', message: 'Please enter a valid email' }
473
+ ]);
474
+ }
312
475
  });
476
+
477
+ checkout.render();
313
478
  ```
314
479
 
315
- Or with string event names:
480
+ ### React Example
316
481
 
317
- ```ts
318
- checkout.on('loaded', () => {});
319
- checkout.on('success', (payload) => {
320
- console.log('Payment successful', payload.payment_method);
321
- });
322
- checkout.on('error', (err) => {
323
- console.error('Payment error', err);
324
- });
325
- checkout.on('expired', () => {});
482
+ ```tsx
483
+ import { useEffect, useRef, useState } from 'react';
484
+ import { PaypercutCheckout, CheckoutInstance, SdkEvent } from '@paypercut/checkout-js';
485
+
486
+ export function CheckoutWithForm({ checkoutId }: { checkoutId: string }) {
487
+ const containerRef = useRef<HTMLDivElement>(null);
488
+ const checkoutRef = useRef<CheckoutInstance | null>(null);
489
+ const [email, setEmail] = useState('');
490
+ const [emailError, setEmailError] = useState('');
491
+
492
+ useEffect(() => {
493
+ if (!containerRef.current) return;
494
+
495
+ const checkout = PaypercutCheckout({
496
+ id: checkoutId,
497
+ containerId: containerRef.current,
498
+ wallet_options: ['apple_pay', 'google_pay']
499
+ });
500
+
501
+ // Handle wallet form validation
502
+ checkout.on(SdkEvent.FormValidation, ({ wallet }) => {
503
+ const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
504
+
505
+ if (isValid) {
506
+ setEmailError('');
507
+ checkout.completeFormValidation(wallet);
508
+ } else {
509
+ setEmailError('Please enter a valid email address');
510
+ checkout.failFormValidation(wallet, [
511
+ { code: 'invalid_email', message: 'Invalid email' }
512
+ ]);
513
+ }
514
+ });
515
+
516
+ checkout.on('success', (payload) => {
517
+ console.log('Payment successful:', payload.payment_method);
518
+ });
519
+
520
+ checkout.render();
521
+ checkoutRef.current = checkout;
522
+
523
+ return () => checkout.destroy();
524
+ }, [checkoutId, email]);
525
+
526
+ return (
527
+ <div>
528
+ {/* Your custom form fields */}
529
+ <div>
530
+ <label htmlFor="email">Email</label>
531
+ <input
532
+ id="email"
533
+ type="email"
534
+ value={email}
535
+ onChange={(e) => setEmail(e.target.value)}
536
+ placeholder="your@email.com"
537
+ />
538
+ {emailError && <span style={{ color: 'red' }}>{emailError}</span>}
539
+ </div>
540
+
541
+ {/* Checkout iframe */}
542
+ <div ref={containerRef} />
543
+ </div>
544
+ );
545
+ }
326
546
  ```
327
547
 
548
+ ### Timeout Behavior
328
549
 
329
- ## Types
550
+ If you don't respond to the `form_validation` event within 10 seconds, the SDK will:
551
+ 1. Emit an `error` event with code `form_validation_timeout`
552
+ 2. Block the wallet payment from starting
330
553
 
331
- ### Payload types
554
+ This ensures the checkout doesn't hang indefinitely if the handler is not implemented.
332
555
 
333
- #### PaymentSuccessPayload
556
+ ### When to Use
334
557
 
335
- ```ts
336
- // Payload for `checkout.on('success', (payload))`
337
- type PaymentSuccessPayload = {
338
- payment_method: {
339
- brand: string; // e.g., 'visa', 'mastercard'
340
- last4: string;
341
- exp_month: number;
342
- exp_year: number;
343
- };
344
- };
558
+ Use form validation when you have:
559
+ - Custom email/phone fields outside the checkout iframe
560
+ - Shipping address forms that must be filled before payment
561
+ - Terms & conditions checkboxes that must be accepted
562
+ - Any other merchant-side validation requirements
563
+
564
+ If you don't need to validate anything, you can simply auto-approve:
565
+
566
+ ```typescript
567
+ checkout.on('form_validation', ({ wallet }) => {
568
+ // No validation needed - always allow wallet to proceed
569
+ checkout.completeFormValidation(wallet);
570
+ });
345
571
  ```
346
572
 
347
- #### ApiErrorPayload (common shapes)
573
+ ---
574
+
575
+ ## Types
576
+
577
+ ### ApiErrorPayload
348
578
 
349
579
  The SDK forwards the error payload provided by Hosted Checkout. Common shapes include:
350
580
 
@@ -505,15 +735,19 @@ Tip: Prefer subscribing with the SdkEvent enum for stronger typing.
505
735
  <body>
506
736
  <div id="checkout"></div>
507
737
 
508
- <script src="https://cdn.jsdelivr.net/npm/@paypercut/checkout-js@1.0.11/dist/paypercut-checkout.iife.min.js"></script>
738
+ <script src="https://cdn.jsdelivr.net/npm/@paypercut/checkout-js@1.0.13/dist/paypercut-checkout.iife.min.js"></script>
509
739
  <script>
510
740
  const checkout = PaypercutCheckout({
511
741
  id: 'CHK_12345',
512
- containerId: '#checkout'
742
+ containerId: '#checkout',
743
+ locale: 'en',
744
+ ui_mode: 'embedded',
745
+ wallet_options: ['apple_pay', 'google_pay']
513
746
  });
514
747
 
515
- checkout.on('success', function() {
516
- alert('Payment successful!');
748
+ checkout.on('success', function(payload) {
749
+ // payload.payment_method: { brand, last4, exp_month, exp_year }
750
+ alert('Payment successful with ' + payload.payment_method.brand + ' ending in ' + payload.payment_method.last4);
517
751
  });
518
752
 
519
753
  checkout.on('error', function(error) {
@@ -533,11 +767,17 @@ import { PaypercutCheckout } from '@paypercut/checkout-js';
533
767
 
534
768
  const checkout = PaypercutCheckout({
535
769
  id: 'CHK_12345',
536
- containerId: '#checkout'
770
+ containerId: '#checkout',
771
+ locale: 'auto',
772
+ ui_mode: 'embedded',
773
+ wallet_options: ['apple_pay', 'google_pay'],
774
+ form_only: false
537
775
  });
538
776
 
539
- checkout.on('success', () => {
777
+ checkout.on('success', (payload) => {
778
+ // payload.payment_method: { brand, last4, exp_month, exp_year }
540
779
  console.log('Payment successful');
780
+ console.log(`Paid with ${payload.payment_method.brand} **** ${payload.payment_method.last4}`);
541
781
  // Redirect to success page
542
782
  window.location.href = '/success';
543
783
  });
@@ -557,17 +797,29 @@ checkout.render();
557
797
  import { useEffect, useRef, useState } from 'react';
558
798
  import { PaypercutCheckout, CheckoutInstance } from '@paypercut/checkout-js';
559
799
 
800
+ interface PaymentMethod {
801
+ brand: string;
802
+ last4: string;
803
+ exp_month: number;
804
+ exp_year: number;
805
+ }
806
+
560
807
  export function CheckoutComponent({ checkoutId }: { checkoutId: string }) {
561
808
  const containerRef = useRef<HTMLDivElement>(null);
562
809
  const checkoutRef = useRef<CheckoutInstance | null>(null);
563
810
  const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
811
+ const [paymentMethod, setPaymentMethod] = useState<PaymentMethod | null>(null);
564
812
 
565
813
  useEffect(() => {
566
814
  if (!containerRef.current) return;
567
815
 
568
816
  const checkout = PaypercutCheckout({
569
817
  id: checkoutId,
570
- containerId: containerRef.current
818
+ containerId: containerRef.current,
819
+ locale: 'auto',
820
+ ui_mode: 'embedded',
821
+ wallet_options: ['apple_pay', 'google_pay'],
822
+ form_only: false
571
823
  });
572
824
 
573
825
  checkout.on('loaded', () => {
@@ -575,9 +827,10 @@ export function CheckoutComponent({ checkoutId }: { checkoutId: string }) {
575
827
  console.log('Checkout loaded');
576
828
  });
577
829
 
578
- checkout.on('success', () => {
830
+ checkout.on('success', (payload) => {
579
831
  setStatus('success');
580
- console.log('Payment successful');
832
+ setPaymentMethod(payload.payment_method);
833
+ console.log('Payment successful:', payload.payment_method);
581
834
  });
582
835
 
583
836
  checkout.on('error', (error) => {
@@ -597,7 +850,9 @@ export function CheckoutComponent({ checkoutId }: { checkoutId: string }) {
597
850
  <div>
598
851
  <div ref={containerRef} style={{ width: '100%', height: '600px' }} />
599
852
  {status === 'loading' && <p>Processing payment...</p>}
600
- {status === 'success' && <p>✅ Payment successful!</p>}
853
+ {status === 'success' && paymentMethod && (
854
+ <p>✅ Payment successful with {paymentMethod.brand} ending in {paymentMethod.last4}!</p>
855
+ )}
601
856
  {status === 'error' && <p>❌ Payment failed. Please try again.</p>}
602
857
  </div>
603
858
  );