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

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,37 @@ import type {
7
7
  StartPayment,
8
8
  SeamlessArguments,
9
9
  OneTimeTokenInfo,
10
+ TokenCollectedData,
11
+ HeadlessTokenResponse,
12
+ ThreeDSecureChallengeResponse,
10
13
  } from './core/types';
11
14
  import { YunoStatus, YunoLanguage, CardFlow } from './core/enums';
12
15
 
13
16
  const LINKING_ERROR =
14
- `The package '@y.uno/yuno-sdk-react-native' doesn't seem to be linked. Make sure: \n\n` +
17
+ `The package '@yuno/yuno-sdk-react-native' doesn't seem to be linked. Make sure: \n\n` +
15
18
  Platform.select({ ios: "- Run 'pod install'\n", default: '' }) +
16
19
  '- You rebuilt the app after installing the package\n' +
17
20
  '- You are not using Expo Go\n';
18
21
 
19
- const YunoSdkNative = NativeModules.YunoSdk
20
- ? NativeModules.YunoSdk
21
- : new Proxy(
22
- {},
23
- {
24
- get() {
25
- throw new Error(LINKING_ERROR);
26
- },
27
- }
28
- );
22
+ /**
23
+ * Devuelve el módulo nativo YunoSdk o lanza un error de linking
24
+ */
25
+ function getYunoNative(): any {
26
+ const native = NativeModules.YunoSdk;
27
+ if (!native) {
28
+ throw new Error(LINKING_ERROR);
29
+ }
30
+ return native;
31
+ }
29
32
 
30
33
  /**
31
- * Event emitter for Yuno SDK events.
34
+ * Devuelve un NativeEventEmitter para el módulo YunoSdk
35
+ * Se crea sólo cuando hace falta (no en top-level).
32
36
  */
33
- const eventEmitter = new NativeEventEmitter(YunoSdkNative);
37
+ function getYunoEventEmitter(): NativeEventEmitter {
38
+ const native = getYunoNative();
39
+ return new NativeEventEmitter(native);
40
+ }
34
41
 
35
42
  /**
36
43
  * Payment state event data.
@@ -85,13 +92,16 @@ export class YunoSdk {
85
92
  /**
86
93
  * Marks the SDK as initialized without calling the native initialize method.
87
94
  * This is useful when the SDK has been initialized from the native side (e.g., in MainActivity/YunoActivity).
88
- *
95
+ *
89
96
  * @param countryCode - ISO country code (e.g., 'US', 'BR', 'CO')
90
97
  * @param language - Optional language setting (defaults to EN)
91
- *
98
+ *
92
99
  * @internal
93
100
  */
94
- static markAsInitialized(countryCode: string = 'CO', language: YunoLanguage = YunoLanguage.EN): void {
101
+ static markAsInitialized(
102
+ countryCode: string = 'CO',
103
+ language: YunoLanguage = YunoLanguage.EN
104
+ ): void {
95
105
  this.countryCode = countryCode;
96
106
  this.language = language;
97
107
  this.isInitialized = true;
@@ -153,7 +163,7 @@ export class YunoSdk {
153
163
  this.countryCode = countryCode;
154
164
  this.language = config.lang!;
155
165
 
156
- await YunoSdkNative.initialize(
166
+ await getYunoNative().initialize(
157
167
  apiKey,
158
168
  countryCode,
159
169
  config,
@@ -186,7 +196,7 @@ export class YunoSdk {
186
196
  countryCode: params.countryCode ?? this.getCountryCode(),
187
197
  };
188
198
 
189
- return YunoSdkNative.enrollmentPayment(args);
199
+ return getYunoNative().enrollmentPayment(args);
190
200
  }
191
201
 
192
202
  /**
@@ -214,7 +224,7 @@ export class YunoSdk {
214
224
  this.checkInitialized();
215
225
 
216
226
  const code = countryCode ?? this.getCountryCode();
217
- return YunoSdkNative.startPaymentLite(params, code);
227
+ return getYunoNative().startPaymentLite(params, code);
218
228
  }
219
229
 
220
230
  /**
@@ -229,7 +239,7 @@ export class YunoSdk {
229
239
  */
230
240
  static async startPayment(showPaymentStatus: boolean = true): Promise<void> {
231
241
  this.checkInitialized();
232
- return YunoSdkNative.startPayment(showPaymentStatus);
242
+ return getYunoNative().startPayment(showPaymentStatus);
233
243
  }
234
244
 
235
245
  /**
@@ -251,7 +261,11 @@ export class YunoSdk {
251
261
  ): Promise<void> {
252
262
  this.checkInitialized();
253
263
  const code = countryCode ?? this.getCountryCode();
254
- return YunoSdkNative.continuePayment(checkoutSession, code, showPaymentStatus);
264
+ return getYunoNative().continuePayment(
265
+ checkoutSession,
266
+ code,
267
+ showPaymentStatus
268
+ );
255
269
  }
256
270
 
257
271
  /**
@@ -284,7 +298,7 @@ export class YunoSdk {
284
298
  countryCode: params.countryCode ?? this.getCountryCode(),
285
299
  };
286
300
 
287
- const statusString = await YunoSdkNative.startPaymentSeamlessLite(
301
+ const statusString = await getYunoNative().startPaymentSeamlessLite(
288
302
  args,
289
303
  this.getLanguage()
290
304
  );
@@ -302,7 +316,7 @@ export class YunoSdk {
302
316
  */
303
317
  static async hideLoader(): Promise<void> {
304
318
  this.checkInitialized();
305
- return YunoSdkNative.hideLoader();
319
+ return getYunoNative().hideLoader();
306
320
  }
307
321
 
308
322
  /**
@@ -326,7 +340,7 @@ export class YunoSdk {
326
340
  }
327
341
 
328
342
  this.checkInitialized();
329
- return YunoSdkNative.receiveDeeplink(url);
343
+ return getYunoNative().receiveDeeplink(url);
330
344
  }
331
345
 
332
346
  /**
@@ -346,7 +360,7 @@ export class YunoSdk {
346
360
  * ```
347
361
  */
348
362
  static async getLastOneTimeToken(): Promise<string | null> {
349
- return YunoSdkNative.getLastOneTimeToken();
363
+ return getYunoNative().getLastOneTimeToken();
350
364
  }
351
365
 
352
366
  /**
@@ -363,16 +377,16 @@ export class YunoSdk {
363
377
  * ```
364
378
  */
365
379
  static async getLastOneTimeTokenInfo(): Promise<OneTimeTokenInfo | null> {
366
- return YunoSdkNative.getLastOneTimeTokenInfo();
380
+ return getYunoNative().getLastOneTimeTokenInfo();
367
381
  }
368
382
 
369
383
  /**
370
384
  * Clears the last OTT tokens stored by the SDK.
371
385
  * This is useful to ensure clean state before starting a new payment flow.
372
- *
386
+ *
373
387
  * Note: This is automatically called at the start of each payment/enrollment flow,
374
388
  * but you can call it manually if needed.
375
- *
389
+ *
376
390
  * @example
377
391
  * ```typescript
378
392
  * // Clear OTT before starting a new payment
@@ -380,7 +394,25 @@ export class YunoSdk {
380
394
  * ```
381
395
  */
382
396
  static async clearLastOneTimeToken(): Promise<void> {
383
- return YunoSdkNative.clearLastOneTimeToken();
397
+ return getYunoNative().clearLastOneTimeToken();
398
+ }
399
+
400
+ /**
401
+ * Clears the last stored payment status.
402
+ * This should be called at the start of each new payment flow
403
+ * to prevent stale status from previous flows affecting new transactions.
404
+ *
405
+ * @returns Promise that resolves when the status is cleared
406
+ *
407
+ * @example
408
+ * ```typescript
409
+ * // Clear payment status before starting a new flow
410
+ * await YunoSdk.clearLastPaymentStatus();
411
+ * await YunoSdk.startPayment({ checkoutSession, countryCode });
412
+ * ```
413
+ */
414
+ static async clearLastPaymentStatus(): Promise<void> {
415
+ return getYunoNative().clearLastPaymentStatus();
384
416
  }
385
417
 
386
418
  /**
@@ -408,10 +440,8 @@ export class YunoSdk {
408
440
  static onPaymentStatus(listener: (state: YunoPaymentState) => void): {
409
441
  remove: () => void;
410
442
  } {
411
- const subscription = eventEmitter.addListener(
412
- 'YunoPaymentStatus',
413
- listener
414
- );
443
+ const emitter = getYunoEventEmitter();
444
+ const subscription = emitter.addListener('YunoPaymentStatus', listener);
415
445
  return { remove: () => subscription.remove() };
416
446
  }
417
447
 
@@ -440,10 +470,8 @@ export class YunoSdk {
440
470
  static onEnrollmentStatus(listener: (state: YunoEnrollmentState) => void): {
441
471
  remove: () => void;
442
472
  } {
443
- const subscription = eventEmitter.addListener(
444
- 'YunoEnrollmentStatus',
445
- listener
446
- );
473
+ const emitter = getYunoEventEmitter();
474
+ const subscription = emitter.addListener('YunoEnrollmentStatus', listener);
447
475
  return { remove: () => subscription.remove() };
448
476
  }
449
477
 
@@ -466,7 +494,8 @@ export class YunoSdk {
466
494
  static onOneTimeToken(listener: (token: string) => void): {
467
495
  remove: () => void;
468
496
  } {
469
- const subscription = eventEmitter.addListener('YunoOneTimeToken', listener);
497
+ const emitter = getYunoEventEmitter();
498
+ const subscription = emitter.addListener('YunoOneTimeToken', listener);
470
499
  return { remove: () => subscription.remove() };
471
500
  }
472
501
 
@@ -490,7 +519,8 @@ export class YunoSdk {
490
519
  static onOneTimeTokenInfo(listener: (tokenInfo: OneTimeTokenInfo) => void): {
491
520
  remove: () => void;
492
521
  } {
493
- const subscription = eventEmitter.addListener('YunoOneTimeTokenInfo', listener);
522
+ const emitter = getYunoEventEmitter();
523
+ const subscription = emitter.addListener('YunoOneTimeTokenInfo', listener);
494
524
  return { remove: () => subscription.remove() };
495
525
  }
496
526
 
@@ -515,4 +545,94 @@ export class YunoSdk {
515
545
  }
516
546
  return this.language;
517
547
  }
548
+
549
+ // ==================== HEADLESS PAYMENT FLOW ====================
550
+
551
+ /**
552
+ * Generate a one-time token (OTT) from collected payment data using the headless flow.
553
+ * This method allows you to tokenize payment information without using the UI components.
554
+ *
555
+ * @param tokenCollectedData The payment data to tokenize
556
+ * @param checkoutSession The checkout session ID
557
+ * @param countryCode The country code for the payment (optional, uses initialized value if not provided)
558
+ * @returns Promise resolving to the generated token or rejecting with an error
559
+ *
560
+ * @example
561
+ * ```typescript
562
+ * import { YunoSdk, CardType } from '@yuno-payments/yuno-sdk-react-native';
563
+ *
564
+ * try {
565
+ * const result = await YunoSdk.generateToken({
566
+ * checkoutSession: '73ed16c5-4481-4dce-af42-404b68e21027',
567
+ * paymentMethod: {
568
+ * type: 'CARD',
569
+ * vaultedToken: null,
570
+ * card: {
571
+ * save: false,
572
+ * detail: {
573
+ * expirationMonth: 11,
574
+ * expirationYear: 25,
575
+ * number: '4000000000001091',
576
+ * securityCode: '123',
577
+ * holderName: 'John Doe',
578
+ * type: CardType.CREDIT,
579
+ * },
580
+ * },
581
+ * },
582
+ * }, 'checkoutSessionId', 'BR');
583
+ *
584
+ * console.log('Token:', result.token);
585
+ * } catch (error) {
586
+ * console.error('Token generation failed:', error);
587
+ * }
588
+ * ```
589
+ */
590
+ static async generateToken(
591
+ tokenCollectedData: TokenCollectedData,
592
+ checkoutSession: string,
593
+ countryCode?: string
594
+ ): Promise<HeadlessTokenResponse> {
595
+ const native = getYunoNative();
596
+ const country = countryCode || this.getCountryCode();
597
+
598
+ return native.generateToken(tokenCollectedData, checkoutSession, country);
599
+ }
600
+
601
+ /**
602
+ * Get the 3D Secure challenge URL for a checkout session using the headless flow.
603
+ * This is typically called after successfully generating a token to handle 3DS verification.
604
+ *
605
+ * @param checkoutSession The checkout session ID
606
+ * @param countryCode The country code for the payment (optional, uses initialized value if not provided)
607
+ * @returns Promise resolving to the 3DS challenge response
608
+ *
609
+ * @example
610
+ * ```typescript
611
+ * import { YunoSdk } from '@yuno-payments/yuno-sdk-react-native';
612
+ *
613
+ * try {
614
+ * // First generate token
615
+ * const tokenResult = await YunoSdk.generateToken(paymentData, checkoutSession, 'BR');
616
+ *
617
+ * // Then get 3DS challenge URL if needed
618
+ * const challengeResult = await YunoSdk.getThreeDSecureChallenge(checkoutSession, 'BR');
619
+ *
620
+ * if (challengeResult.type === 'URL') {
621
+ * console.log('3DS URL:', challengeResult.data);
622
+ * // Open this URL in a WebView for the user to complete 3DS verification
623
+ * }
624
+ * } catch (error) {
625
+ * console.error('3DS challenge failed:', error);
626
+ * }
627
+ * ```
628
+ */
629
+ static async getThreeDSecureChallenge(
630
+ checkoutSession: string,
631
+ countryCode?: string
632
+ ): Promise<ThreeDSecureChallengeResponse> {
633
+ const native = getYunoNative();
634
+ const country = countryCode || this.getCountryCode();
635
+
636
+ return native.getThreeDSecureChallenge(checkoutSession, country);
637
+ }
518
638
  }
@@ -0,0 +1,89 @@
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
+ }
@@ -204,4 +204,3 @@ export interface OneTimeTokenInfo {
204
204
  */
205
205
  customer?: CustomerPayerInformation | null;
206
206
  }
207
-
@@ -5,3 +5,17 @@ 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
+ } from './HeadlessTypes';