Skip to content

Commit 0fe396d

Browse files
Feature(SDK-106): Add unionpay as a supported network (#42)
* feat: support union pay * test: remove un-needed tests
1 parent b6ba487 commit 0fe396d

19 files changed

Lines changed: 246 additions & 28 deletions

android/src/main/java/com/moyasarsdk/samsungpay/SamsungPayButtonViewModel.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,12 @@ class SamsungPayButtonViewModel(
209209

210210
// Map string network names to Samsung Pay SDK Brand enums
211211
merchantInfo.supportedNetworks.forEach { network ->
212-
when (network) {
212+
when (network.lowercase()) {
213213
"mada" -> brandList.add(SpaySdk.Brand.MADA)
214214
"visa" -> brandList.add(SpaySdk.Brand.VISA)
215215
"mastercard" -> brandList.add(SpaySdk.Brand.MASTERCARD)
216216
"amex" -> brandList.add(SpaySdk.Brand.AMERICANEXPRESS)
217+
"unionpay", "chinaunionpay" -> brandList.add(SpaySdk.Brand.CHINAUNIONPAY)
217218
else -> Logger.w("MoyasarSDK", "Unknown card network: $network, skipping")
218219
}
219220
}

example/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const paymentConfig = new PaymentConfig({
3131
merchantCountryCode: 'SA',
3232
description: 'Test payment',
3333
metadata: { size: '250 g' },
34-
supportedNetworks: ['mada', 'visa', 'mastercard', 'amex'],
34+
supportedNetworks: ['mada', 'visa', 'mastercard', 'amex', 'unionpay'],
3535
creditCard: new CreditCardConfig({ saveCard: false, manual: false }),
3636
applePay: new ApplePayConfig({
3737
merchantId: 'merchant.mysr.aalrabiah',

ios/ReactNativeApplePay/ReactNativePayments.m

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ - (NSArray *_Nonnull)getSupportedNetworksFromMethodData:(NSDictionary *_Nonnull)
237237

238238
if (iOSVersion >= 9.2) {
239239
[supportedNetworksMapping setObject:PKPaymentNetworkChinaUnionPay forKey:@"chinaunionpay"];
240+
[supportedNetworksMapping setObject:PKPaymentNetworkChinaUnionPay forKey:@"unionpay"];
240241
[supportedNetworksMapping setObject:PKPaymentNetworkInterac forKey:@"interac"];
241242
}
242243

@@ -267,7 +268,12 @@ - (NSArray *_Nonnull)getSupportedNetworksFromMethodData:(NSDictionary *_Nonnull)
267268
NSArray *jsSupportedNetworks = methodData[@"supportedNetworks"];
268269
NSMutableArray *supportedNetworks = [NSMutableArray array];
269270
for (NSString *supportedNetwork in jsSupportedNetworks) {
270-
[supportedNetworks addObject: supportedNetworksMapping[supportedNetwork]];
271+
NSString *networkKey = [supportedNetwork lowercaseString];
272+
id mappedNetwork = supportedNetworksMapping[networkKey];
273+
274+
if (mappedNetwork != nil) {
275+
[supportedNetworks addObject:mappedNetwork];
276+
}
271277
}
272278

273279
return supportedNetworks;

src/__tests__/__fixtures__/payment_config_fixture.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const paymentConfigWithoutSaveOnlyFixture = new PaymentConfig({
1212
oversize: true,
1313
length: 42,
1414
},
15-
supportedNetworks: ['mada', 'mastercard', 'visa'],
15+
supportedNetworks: ['mada', 'mastercard', 'visa', 'unionpay'],
1616
applePay: new ApplePayConfig({
1717
merchantId: 'com.example.merchant',
1818
label: 'Example Merchant',
@@ -35,7 +35,7 @@ export const paymentConfigWithSaveOnlyFixture = new PaymentConfig({
3535
oversize: true,
3636
length: 42,
3737
},
38-
supportedNetworks: ['mada', 'mastercard', 'visa'],
38+
supportedNetworks: ['mada', 'mastercard', 'visa', 'unionpay'],
3939
applePay: new ApplePayConfig({
4040
merchantId: 'com.example.merchant',
4141
label: 'Example Merchant',

src/__tests__/helpers/credit_card_utils.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
isValidLuhn,
33
getCreditCardNetworkFromNumber,
4+
mapCardNetworkStrings,
45
} from '../../helpers/credit_card_utils';
56
import { CreditCardNetwork } from '../../models/credit_card_network';
67

@@ -10,13 +11,15 @@ describe('isValidLuhn', () => {
1011
expect(isValidLuhn('4532015112830366')).toBe(true); // Valid Visa
1112
expect(isValidLuhn('378282246310005')).toBe(true); // Valid Amex
1213
expect(isValidLuhn('5421080101000000')).toBe(true); // Valid Mastercard
14+
expect(isValidLuhn('6200000000000005')).toBe(true); // Valid UnionPay
1315
});
1416

1517
it('should return false for an invalid card number', () => {
1618
expect(isValidLuhn('4201320111111011')).toBe(false); // Invalid Mada
1719
expect(isValidLuhn('4532015112830367')).toBe(false); // Invalid Visa
1820
expect(isValidLuhn('378282246310006')).toBe(false); // Invalid Amex
1921
expect(isValidLuhn('5421080101000001')).toBe(false); // Invalid Mastercard
22+
expect(isValidLuhn('6200000000000006')).toBe(false); // Invalid UnionPay
2023
});
2124

2225
it('should return false for a card number with non-digit characters', () => {
@@ -60,9 +63,48 @@ describe('getCreditCardNetworkFromNumber', () => {
6063
);
6164
});
6265

66+
it('should return unionpay for a card number matching unionpay range and length', () => {
67+
expect(getCreditCardNetworkFromNumber('6200000000000005')).toBe(
68+
CreditCardNetwork.unionpay
69+
);
70+
expect(getCreditCardNetworkFromNumber('6200000000000000000')).toBe(
71+
CreditCardNetwork.unionpay
72+
);
73+
});
74+
6375
it('should return unknown for a card number not matching any known network', () => {
6476
expect(getCreditCardNetworkFromNumber('1234567812345670')).toBe(
6577
CreditCardNetwork.unknown
6678
);
6779
});
6880
});
81+
82+
describe('mapCardNetworkStrings', () => {
83+
it('should map all supported network aliases including UnionPay aliases', () => {
84+
const mappedNetworks = mapCardNetworkStrings([
85+
'mada',
86+
'visa',
87+
'mastercard',
88+
'amex',
89+
'unionpay',
90+
'chinaunionpay',
91+
'china union pay',
92+
]);
93+
94+
expect(mappedNetworks).toEqual([
95+
CreditCardNetwork.mada,
96+
CreditCardNetwork.visa,
97+
CreditCardNetwork.master,
98+
CreditCardNetwork.amex,
99+
CreditCardNetwork.unionpay,
100+
CreditCardNetwork.unionpay,
101+
CreditCardNetwork.unionpay,
102+
]);
103+
});
104+
105+
it('should filter out unsupported network names', () => {
106+
const mappedNetworks = mapCardNetworkStrings(['visa', 'unsupported']);
107+
108+
expect(mappedNetworks).toEqual([CreditCardNetwork.visa]);
109+
});
110+
});

src/__tests__/helpers/formatters.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ describe('formatCreditCardNumber', () => {
2020
expect(formattedNumber).toBe('3782 822463 10005');
2121
});
2222

23+
it('should format 19 digits (unionpay) card correctly', () => {
24+
const formattedNumber = formatCreditCardNumber('6200000000000000000');
25+
expect(formattedNumber).toBe('6200 0000 0000 0000 000');
26+
});
27+
2328
it('should return the original number if it does not match any pattern', () => {
2429
const formattedNumber = formatCreditCardNumber('411');
2530
expect(formattedNumber).toBe('411');
@@ -53,6 +58,14 @@ describe('formatCreditCardNumber', () => {
5358
expect(formattedNumber4).toBe('3782 822463 1000');
5459
});
5560

61+
it('should format incompleted 19 digits (unionpay) card correctly', () => {
62+
const formattedNumber = formatCreditCardNumber('62000000000000000');
63+
expect(formattedNumber).toBe('6200 0000 0000 0000 0');
64+
65+
const formattedNumber2 = formatCreditCardNumber('620000000000000000');
66+
expect(formattedNumber2).toBe('6200 0000 0000 0000 00');
67+
});
68+
5669
describe('formatMobileNumber', () => {
5770
it('should format numbers with length <= 3 correctly', () => {
5871
const formattedNumber = formatMobileNumber({ cleanedNumber: '051' });

src/__tests__/models/payment_config.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ describe('PaymentConfig', () => {
2020
'visa',
2121
'mastercard',
2222
'amex',
23+
'unionpay',
2324
]);
2425
expect(config.applePay).toBeUndefined();
2526
expect(config.creditCard).toBeInstanceOf(CreditCardConfig);

src/__tests__/models/payment_request.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,49 @@ describe('PaymentRequest', () => {
154154
});
155155
});
156156

157+
it('should convert PaymentRequest to json with UnionPay company value', () => {
158+
const ccSource = new CreditCardRequestSource({
159+
name: 'John Doe',
160+
number: '6200000000000005',
161+
cvc: '123',
162+
month: '12',
163+
year: '2028',
164+
});
165+
166+
const paymentRequest = new PaymentRequest({
167+
givenId: '123e4567-e89b-12d3-a456-426614174000',
168+
amount: 1000,
169+
currency: 'USD',
170+
description: 'Test payment',
171+
metadata: { orderId: 12345 },
172+
source: ccSource,
173+
callbackUrl: 'https://example.com/callback',
174+
});
175+
176+
const json = paymentRequest.toJson();
177+
178+
expect(json).toEqual({
179+
given_id: '123e4567-e89b-12d3-a456-426614174000',
180+
amount: 1000,
181+
currency: 'USD',
182+
description: 'Test payment',
183+
metadata: { orderId: 12345 },
184+
source: {
185+
type: 'creditcard',
186+
company: 'unionpay',
187+
name: 'John Doe',
188+
number: '6200000000000005',
189+
cvc: '123',
190+
month: '12',
191+
year: '2028',
192+
save_card: 'false',
193+
manual: 'false',
194+
},
195+
callback_url: 'https://example.com/callback',
196+
apply_coupon: true,
197+
});
198+
});
199+
157200
it('should correctly apply the false coupon param', () => {
158201
const ccSource = new CreditCardRequestSource({
159202
name: 'John Doe',

src/__tests__/services/credit_card_payment_service.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const allNetworks = [
2727
CreditCardNetwork.visa,
2828
CreditCardNetwork.master,
2929
CreditCardNetwork.amex,
30+
CreditCardNetwork.unionpay,
3031
];
3132

3233
describe('CreditCardPaymentService', () => {
@@ -53,6 +54,13 @@ describe('CreditCardPaymentService', () => {
5354
cvc: '12',
5455
};
5556

57+
const validUnionPayFields: CreditCardFields = {
58+
name: 'John Doe',
59+
number: '6200000000000000000',
60+
expiry: `12/${new Date().getFullYear() + 1}`,
61+
cvc: '123',
62+
};
63+
5664
beforeEach(() => {
5765
service = new CreditCardPaymentService();
5866
});
@@ -349,5 +357,14 @@ describe('CreditCardPaymentService', () => {
349357

350358
expect(result).toBe(false);
351359
});
360+
361+
it('should return true if unionpay fields are valid', () => {
362+
const result = service.validateAllFields(
363+
validUnionPayFields,
364+
allNetworks
365+
);
366+
367+
expect(result).toBe(true);
368+
});
352369
});
353370
});

src/__tests__/services/validators/credit_card_cvc_validator.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ describe('CreditCardCvcValidator', () => {
2828

2929
const result2 = validator.validate('123', '4201320111111010');
3030
expect(result2).toBeNull();
31+
32+
const result3 = validator.validate('123', '6200000000000005');
33+
expect(result3).toBeNull();
3134
});
3235

3336
it('should return error for empty CVC', () => {
@@ -68,5 +71,8 @@ describe('CreditCardCvcValidator', () => {
6871

6972
const result3 = validator.validate('12', '4111111111111111');
7073
expect(result3).toContain('invalidCvc');
74+
75+
const result4 = validator.validate('1234', '6200000000000005');
76+
expect(result4).toContain('invalidCvc');
7177
});
7278
});

0 commit comments

Comments
 (0)