@yuno-payments/yuno-sdk-react-native 1.0.16 → 1.0.17-rc.10

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/src/YunoSdk.ts CHANGED
@@ -7,30 +7,39 @@ import type {
7
7
  StartPayment,
8
8
  SeamlessArguments,
9
9
  OneTimeTokenInfo,
10
+ TokenCollectedData,
11
+ HeadlessTokenResponse,
12
+ ThreeDSecureChallengeResponse,
13
+ EnrollmentCollectedData,
14
+ HeadlessEnrollmentResponse,
10
15
  } from './core/types';
11
16
  import { YunoStatus, YunoLanguage, CardFlow } from './core/enums';
12
17
 
13
18
  const LINKING_ERROR =
14
- `The package '@y.uno/yuno-sdk-react-native' doesn't seem to be linked. Make sure: \n\n` +
19
+ `The package '@yuno/yuno-sdk-react-native' doesn't seem to be linked. Make sure: \n\n` +
15
20
  Platform.select({ ios: "- Run 'pod install'\n", default: '' }) +
16
21
  '- You rebuilt the app after installing the package\n' +
17
22
  '- You are not using Expo Go\n';
18
23
 
19
- const YunoSdkNative = NativeModules.YunoSdk
20
- ? NativeModules.YunoSdk
21
- : new Proxy(
22
- {},
23
- {
24
- get() {
25
- throw new Error(LINKING_ERROR);
26
- },
27
- }
28
- );
24
+ /**
25
+ * Devuelve el módulo nativo YunoSdk o lanza un error de linking
26
+ */
27
+ function getYunoNative(): any {
28
+ const native = NativeModules.YunoSdk;
29
+ if (!native) {
30
+ throw new Error(LINKING_ERROR);
31
+ }
32
+ return native;
33
+ }
29
34
 
30
35
  /**
31
- * Event emitter for Yuno SDK events.
36
+ * Devuelve un NativeEventEmitter para el módulo YunoSdk
37
+ * Se crea sólo cuando hace falta (no en top-level).
32
38
  */
33
- const eventEmitter = new NativeEventEmitter(YunoSdkNative);
39
+ function getYunoEventEmitter(): NativeEventEmitter {
40
+ const native = getYunoNative();
41
+ return new NativeEventEmitter(native);
42
+ }
34
43
 
35
44
  /**
36
45
  * Payment state event data.
@@ -85,13 +94,16 @@ export class YunoSdk {
85
94
  /**
86
95
  * Marks the SDK as initialized without calling the native initialize method.
87
96
  * This is useful when the SDK has been initialized from the native side (e.g., in MainActivity/YunoActivity).
88
- *
97
+ *
89
98
  * @param countryCode - ISO country code (e.g., 'US', 'BR', 'CO')
90
99
  * @param language - Optional language setting (defaults to EN)
91
- *
100
+ *
92
101
  * @internal
93
102
  */
94
- static markAsInitialized(countryCode: string = 'CO', language: YunoLanguage = YunoLanguage.EN): void {
103
+ static markAsInitialized(
104
+ countryCode: string = 'CO',
105
+ language: YunoLanguage = YunoLanguage.EN
106
+ ): void {
95
107
  this.countryCode = countryCode;
96
108
  this.language = language;
97
109
  this.isInitialized = true;
@@ -153,7 +165,7 @@ export class YunoSdk {
153
165
  this.countryCode = countryCode;
154
166
  this.language = config.lang!;
155
167
 
156
- await YunoSdkNative.initialize(
168
+ await getYunoNative().initialize(
157
169
  apiKey,
158
170
  countryCode,
159
171
  config,
@@ -186,7 +198,7 @@ export class YunoSdk {
186
198
  countryCode: params.countryCode ?? this.getCountryCode(),
187
199
  };
188
200
 
189
- return YunoSdkNative.enrollmentPayment(args);
201
+ return getYunoNative().enrollmentPayment(args);
190
202
  }
191
203
 
192
204
  /**
@@ -214,7 +226,7 @@ export class YunoSdk {
214
226
  this.checkInitialized();
215
227
 
216
228
  const code = countryCode ?? this.getCountryCode();
217
- return YunoSdkNative.startPaymentLite(params, code);
229
+ return getYunoNative().startPaymentLite(params, code);
218
230
  }
219
231
 
220
232
  /**
@@ -229,7 +241,7 @@ export class YunoSdk {
229
241
  */
230
242
  static async startPayment(showPaymentStatus: boolean = true): Promise<void> {
231
243
  this.checkInitialized();
232
- return YunoSdkNative.startPayment(showPaymentStatus);
244
+ return getYunoNative().startPayment(showPaymentStatus);
233
245
  }
234
246
 
235
247
  /**
@@ -251,7 +263,11 @@ export class YunoSdk {
251
263
  ): Promise<void> {
252
264
  this.checkInitialized();
253
265
  const code = countryCode ?? this.getCountryCode();
254
- return YunoSdkNative.continuePayment(checkoutSession, code, showPaymentStatus);
266
+ return getYunoNative().continuePayment(
267
+ checkoutSession,
268
+ code,
269
+ showPaymentStatus
270
+ );
255
271
  }
256
272
 
257
273
  /**
@@ -284,7 +300,7 @@ export class YunoSdk {
284
300
  countryCode: params.countryCode ?? this.getCountryCode(),
285
301
  };
286
302
 
287
- const statusString = await YunoSdkNative.startPaymentSeamlessLite(
303
+ const statusString = await getYunoNative().startPaymentSeamlessLite(
288
304
  args,
289
305
  this.getLanguage()
290
306
  );
@@ -302,7 +318,7 @@ export class YunoSdk {
302
318
  */
303
319
  static async hideLoader(): Promise<void> {
304
320
  this.checkInitialized();
305
- return YunoSdkNative.hideLoader();
321
+ return getYunoNative().hideLoader();
306
322
  }
307
323
 
308
324
  /**
@@ -326,7 +342,7 @@ export class YunoSdk {
326
342
  }
327
343
 
328
344
  this.checkInitialized();
329
- return YunoSdkNative.receiveDeeplink(url);
345
+ return getYunoNative().receiveDeeplink(url);
330
346
  }
331
347
 
332
348
  /**
@@ -346,7 +362,7 @@ export class YunoSdk {
346
362
  * ```
347
363
  */
348
364
  static async getLastOneTimeToken(): Promise<string | null> {
349
- return YunoSdkNative.getLastOneTimeToken();
365
+ return getYunoNative().getLastOneTimeToken();
350
366
  }
351
367
 
352
368
  /**
@@ -363,16 +379,16 @@ export class YunoSdk {
363
379
  * ```
364
380
  */
365
381
  static async getLastOneTimeTokenInfo(): Promise<OneTimeTokenInfo | null> {
366
- return YunoSdkNative.getLastOneTimeTokenInfo();
382
+ return getYunoNative().getLastOneTimeTokenInfo();
367
383
  }
368
384
 
369
385
  /**
370
386
  * Clears the last OTT tokens stored by the SDK.
371
387
  * This is useful to ensure clean state before starting a new payment flow.
372
- *
388
+ *
373
389
  * Note: This is automatically called at the start of each payment/enrollment flow,
374
390
  * but you can call it manually if needed.
375
- *
391
+ *
376
392
  * @example
377
393
  * ```typescript
378
394
  * // Clear OTT before starting a new payment
@@ -380,7 +396,25 @@ export class YunoSdk {
380
396
  * ```
381
397
  */
382
398
  static async clearLastOneTimeToken(): Promise<void> {
383
- return YunoSdkNative.clearLastOneTimeToken();
399
+ return getYunoNative().clearLastOneTimeToken();
400
+ }
401
+
402
+ /**
403
+ * Clears the last stored payment status.
404
+ * This should be called at the start of each new payment flow
405
+ * to prevent stale status from previous flows affecting new transactions.
406
+ *
407
+ * @returns Promise that resolves when the status is cleared
408
+ *
409
+ * @example
410
+ * ```typescript
411
+ * // Clear payment status before starting a new flow
412
+ * await YunoSdk.clearLastPaymentStatus();
413
+ * await YunoSdk.startPayment({ checkoutSession, countryCode });
414
+ * ```
415
+ */
416
+ static async clearLastPaymentStatus(): Promise<void> {
417
+ return getYunoNative().clearLastPaymentStatus();
384
418
  }
385
419
 
386
420
  /**
@@ -408,10 +442,8 @@ export class YunoSdk {
408
442
  static onPaymentStatus(listener: (state: YunoPaymentState) => void): {
409
443
  remove: () => void;
410
444
  } {
411
- const subscription = eventEmitter.addListener(
412
- 'YunoPaymentStatus',
413
- listener
414
- );
445
+ const emitter = getYunoEventEmitter();
446
+ const subscription = emitter.addListener('YunoPaymentStatus', listener);
415
447
  return { remove: () => subscription.remove() };
416
448
  }
417
449
 
@@ -440,10 +472,8 @@ export class YunoSdk {
440
472
  static onEnrollmentStatus(listener: (state: YunoEnrollmentState) => void): {
441
473
  remove: () => void;
442
474
  } {
443
- const subscription = eventEmitter.addListener(
444
- 'YunoEnrollmentStatus',
445
- listener
446
- );
475
+ const emitter = getYunoEventEmitter();
476
+ const subscription = emitter.addListener('YunoEnrollmentStatus', listener);
447
477
  return { remove: () => subscription.remove() };
448
478
  }
449
479
 
@@ -466,7 +496,8 @@ export class YunoSdk {
466
496
  static onOneTimeToken(listener: (token: string) => void): {
467
497
  remove: () => void;
468
498
  } {
469
- const subscription = eventEmitter.addListener('YunoOneTimeToken', listener);
499
+ const emitter = getYunoEventEmitter();
500
+ const subscription = emitter.addListener('YunoOneTimeToken', listener);
470
501
  return { remove: () => subscription.remove() };
471
502
  }
472
503
 
@@ -490,7 +521,8 @@ export class YunoSdk {
490
521
  static onOneTimeTokenInfo(listener: (tokenInfo: OneTimeTokenInfo) => void): {
491
522
  remove: () => void;
492
523
  } {
493
- const subscription = eventEmitter.addListener('YunoOneTimeTokenInfo', listener);
524
+ const emitter = getYunoEventEmitter();
525
+ const subscription = emitter.addListener('YunoOneTimeTokenInfo', listener);
494
526
  return { remove: () => subscription.remove() };
495
527
  }
496
528
 
@@ -515,4 +547,149 @@ export class YunoSdk {
515
547
  }
516
548
  return this.language;
517
549
  }
550
+
551
+ // ==================== HEADLESS PAYMENT FLOW ====================
552
+
553
+ /**
554
+ * Generate a one-time token (OTT) from collected payment data using the headless flow.
555
+ * This method allows you to tokenize payment information without using the UI components.
556
+ *
557
+ * @param tokenCollectedData The payment data to tokenize
558
+ * @param checkoutSession The checkout session ID
559
+ * @param countryCode The country code for the payment (optional, uses initialized value if not provided)
560
+ * @returns Promise resolving to the generated token or rejecting with an error
561
+ *
562
+ * @example
563
+ * ```typescript
564
+ * import { YunoSdk, CardType } from '@yuno-payments/yuno-sdk-react-native';
565
+ *
566
+ * try {
567
+ * const result = await YunoSdk.generateToken({
568
+ * checkoutSession: '73ed16c5-4481-4dce-af42-404b68e21027',
569
+ * paymentMethod: {
570
+ * type: 'CARD',
571
+ * vaultedToken: null,
572
+ * card: {
573
+ * save: false,
574
+ * detail: {
575
+ * expirationMonth: 11,
576
+ * expirationYear: 25,
577
+ * number: '4000000000001091',
578
+ * securityCode: '123',
579
+ * holderName: 'John Doe',
580
+ * type: CardType.CREDIT,
581
+ * },
582
+ * },
583
+ * },
584
+ * }, 'checkoutSessionId', 'BR');
585
+ *
586
+ * console.log('Token:', result.token);
587
+ * } catch (error) {
588
+ * console.error('Token generation failed:', error);
589
+ * }
590
+ * ```
591
+ */
592
+ static async generateToken(
593
+ tokenCollectedData: TokenCollectedData,
594
+ checkoutSession: string,
595
+ countryCode?: string
596
+ ): Promise<HeadlessTokenResponse> {
597
+ const native = getYunoNative();
598
+ const country = countryCode || this.getCountryCode();
599
+
600
+ return native.generateToken(tokenCollectedData, checkoutSession, country);
601
+ }
602
+
603
+ /**
604
+ * Get the 3D Secure challenge URL for a checkout session using the headless flow.
605
+ * This is typically called after successfully generating a token to handle 3DS verification.
606
+ *
607
+ * @param checkoutSession The checkout session ID
608
+ * @param countryCode The country code for the payment (optional, uses initialized value if not provided)
609
+ * @returns Promise resolving to the 3DS challenge response
610
+ *
611
+ * @example
612
+ * ```typescript
613
+ * import { YunoSdk } from '@yuno-payments/yuno-sdk-react-native';
614
+ *
615
+ * try {
616
+ * // First generate token
617
+ * const tokenResult = await YunoSdk.generateToken(paymentData, checkoutSession, 'BR');
618
+ *
619
+ * // Then get 3DS challenge URL if needed
620
+ * const challengeResult = await YunoSdk.getThreeDSecureChallenge(checkoutSession, 'BR');
621
+ *
622
+ * if (challengeResult.type === 'URL') {
623
+ * console.log('3DS URL:', challengeResult.data);
624
+ * // Open this URL in a WebView for the user to complete 3DS verification
625
+ * }
626
+ * } catch (error) {
627
+ * console.error('3DS challenge failed:', error);
628
+ * }
629
+ * ```
630
+ */
631
+ static async getThreeDSecureChallenge(
632
+ checkoutSession: string,
633
+ countryCode?: string
634
+ ): Promise<ThreeDSecureChallengeResponse> {
635
+ const native = getYunoNative();
636
+ const country = countryCode || this.getCountryCode();
637
+
638
+ return native.getThreeDSecureChallenge(checkoutSession, country);
639
+ }
640
+
641
+ // ==================== HEADLESS ENROLLMENT FLOW ====================
642
+
643
+ /**
644
+ * Continue enrollment to create a vaulted token from collected payment data using the headless flow.
645
+ * This method allows you to save payment information without using the UI components.
646
+ *
647
+ * @param enrollmentCollectedData The enrollment data to process
648
+ * @param customerSession The customer session ID
649
+ * @param countryCode The country code for the enrollment (optional, uses initialized value if not provided)
650
+ * @returns Promise resolving to the vaulted token or rejecting with an error
651
+ *
652
+ * @example
653
+ * ```typescript
654
+ * import { YunoSdk, CardType } from '@yuno-payments/yuno-sdk-react-native';
655
+ *
656
+ * try {
657
+ * const result = await YunoSdk.continueEnrollment({
658
+ * customerSession: '73ed16c5-4481-4dce-af42-404b68e21027',
659
+ * paymentMethod: {
660
+ * type: 'CARD',
661
+ * card: {
662
+ * save: true,
663
+ * detail: {
664
+ * expirationMonth: 11,
665
+ * expirationYear: 25,
666
+ * number: '4000000000001091',
667
+ * securityCode: '123',
668
+ * holderName: 'John Doe',
669
+ * type: CardType.CREDIT,
670
+ * },
671
+ * },
672
+ * },
673
+ * }, 'customerSessionId', 'BR');
674
+ *
675
+ * console.log('Vaulted Token:', result.vaultedToken);
676
+ * } catch (error) {
677
+ * console.error('Enrollment failed:', error);
678
+ * }
679
+ * ```
680
+ */
681
+ static async continueEnrollment(
682
+ enrollmentCollectedData: EnrollmentCollectedData,
683
+ customerSession: string,
684
+ countryCode?: string
685
+ ): Promise<HeadlessEnrollmentResponse> {
686
+ const native = getYunoNative();
687
+ const country = countryCode || this.getCountryCode();
688
+
689
+ return native.continueEnrollment(
690
+ enrollmentCollectedData,
691
+ customerSession,
692
+ country
693
+ );
694
+ }
518
695
  }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Headless Payment Flow Types
3
+ * These types mirror the native SDK's TokenCollectedData structure
4
+ */
5
+
6
+ export enum CardType {
7
+ CREDIT = 'CREDIT',
8
+ DEBIT = 'DEBIT',
9
+ }
10
+
11
+ export interface Detail {
12
+ expirationMonth?: number;
13
+ expirationYear?: number;
14
+ number?: string;
15
+ securityCode?: string;
16
+ holderName?: string;
17
+ type?: CardType;
18
+ }
19
+
20
+ export interface Installment {
21
+ id: string;
22
+ value: number;
23
+ }
24
+
25
+ export interface CardData {
26
+ save?: boolean;
27
+ detail?: Detail;
28
+ installment?: Installment;
29
+ }
30
+
31
+ export interface Document {
32
+ type: string;
33
+ number: string;
34
+ }
35
+
36
+ export interface Phone {
37
+ countryCode: string;
38
+ number: string;
39
+ }
40
+
41
+ export interface Address {
42
+ street?: string;
43
+ number?: string;
44
+ complement?: string;
45
+ neighborhood?: string;
46
+ city?: string;
47
+ state?: string;
48
+ zipCode?: string;
49
+ country?: string;
50
+ }
51
+
52
+ export interface Customer {
53
+ id: string;
54
+ merchantCustomerId: string;
55
+ firstName?: string;
56
+ lastName?: string;
57
+ dateOfBirth?: string;
58
+ email?: string;
59
+ country?: string;
60
+ createdAt?: string;
61
+ updatedAt?: string;
62
+ document?: Document;
63
+ phone?: Phone;
64
+ billingAddress?: Address;
65
+ shippingAddress?: Address;
66
+ }
67
+
68
+ export interface PaymentMethod {
69
+ type: string;
70
+ vaultedToken?: string | null;
71
+ card?: CardData;
72
+ customer?: Customer;
73
+ }
74
+
75
+ export interface TokenCollectedData {
76
+ checkoutSession?: string;
77
+ customerSession?: string;
78
+ paymentMethod: PaymentMethod;
79
+ }
80
+
81
+ export interface ThreeDSecureChallengeResponse {
82
+ type: string;
83
+ data: string;
84
+ }
85
+
86
+ export interface HeadlessTokenResponse {
87
+ token?: string;
88
+ error?: string;
89
+ }
90
+
91
+ /**
92
+ * Headless Enrollment Flow Types
93
+ * These types mirror the native SDK's EnrollmentCollectedData structure
94
+ */
95
+
96
+ export interface EnrollmentMethod {
97
+ type: string;
98
+ card: CardData;
99
+ customer?: Customer;
100
+ }
101
+
102
+ export interface EnrollmentCollectedData {
103
+ customerSession: string;
104
+ paymentMethod: EnrollmentMethod;
105
+ }
106
+
107
+ export interface HeadlessEnrollmentResponse {
108
+ vaultedToken?: string;
109
+ error?: string;
110
+ }
@@ -204,4 +204,3 @@ export interface OneTimeTokenInfo {
204
204
  */
205
205
  customer?: CustomerPayerInformation | null;
206
206
  }
207
-
@@ -5,3 +5,20 @@ export type { EnrollmentArguments } from './EnrollmentArguments';
5
5
  export type { StartPayment, MethodSelected } from './StartPayment';
6
6
  export type { SeamlessArguments } from './SeamlessArguments';
7
7
  export type { OneTimeTokenInfo } from './OneTimeTokenInfo';
8
+ export type {
9
+ TokenCollectedData,
10
+ PaymentMethod,
11
+ CardData,
12
+ Detail,
13
+ Installment,
14
+ Customer,
15
+ Document,
16
+ Phone,
17
+ Address,
18
+ CardType,
19
+ ThreeDSecureChallengeResponse,
20
+ HeadlessTokenResponse,
21
+ EnrollmentCollectedData,
22
+ EnrollmentMethod,
23
+ HeadlessEnrollmentResponse,
24
+ } from './HeadlessTypes';