Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ object AirwallexPaymentSessionConverter {

val currency = sessionMap.getStringOrNull("currency") ?: error("Currency is required")
val countryCode = sessionMap.getStringOrNull("countryCode") ?: error("Country code is required")
val locale = sessionMap.toLocale()
val paymentIntentId =
sessionMap.getStringOrNull("paymentIntentId") ?: error("PaymentIntentId is required")

Expand Down Expand Up @@ -60,6 +61,7 @@ object AirwallexPaymentSessionConverter {
.setHidePaymentConsents(hidePaymentConsents)
.setPaymentMethods(paymentMethods)
.setShipping(shipping)
.setLocale(locale)
.build()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ object AirwallexRecurringSessionConverter {
val currency = sessionMap.getStringOrNull("currency") ?: error("currency is required")

val countryCode = sessionMap.getStringOrNull("countryCode") ?: error("countryCode is required")
val locale = sessionMap.toLocale()

val amount = sessionMap.getDoubleOrDefault("amount", -1.0).let {
if (it == -1.0) error("amount is required")
Expand Down Expand Up @@ -54,6 +55,7 @@ object AirwallexRecurringSessionConverter {
.setReturnUrl(returnUrl)
.setRequireEmail(isEmailRequired)
.setPaymentMethods(paymentMethods)
.setLocale(locale)

merchantTriggerReason?.let {
sessionBuilder.setMerchantTriggerReason(merchantTriggerReason)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ object AirwallexRecurringWithIntentSessionConverter {

val countryCode = sessionMap.getStringOrNull("countryCode")
?: error("countryCode is required")
val locale = sessionMap.toLocale()

val amount = sessionMap.getDoubleOrDefault("amount", -1.0).let {
if (it == -1.0) error("amount is required")
Expand Down Expand Up @@ -70,6 +71,7 @@ object AirwallexRecurringWithIntentSessionConverter {
.setReturnUrl(returnUrl)
.setAutoCapture(autoCapture)
.setShipping(shipping)
.setLocale(locale)

merchantTriggerReason?.let {
sessionBuilder.setMerchantTriggerReason(merchantTriggerReason)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.airwallex.android.core.model.PaymentMethod
import com.airwallex.android.core.model.Shipping
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import java.util.Locale

fun ReadableMap.getStringOrNull(key: String): String? =
if (hasKey(key)) getString(key) else null
Expand All @@ -26,6 +27,9 @@ fun ReadableMap.getArrayOrNull(key: String): ReadableArray? =
fun ReadableMap.getMapOrNull(key: String): ReadableMap? =
if (hasKey(key)) getMap(key) else null

fun ReadableMap.toLocale(): Locale? =
getStringOrNull("lang")?.let { Locale.forLanguageTag(it.replace('_', '-')) }

fun ReadableMap.toShipping(): Shipping? {
val firstName = getStringOrNull("firstName") ?: getStringOrNull("first_name")
val lastName = getStringOrNull("lastName") ?: getStringOrNull("last_name")
Expand Down
62 changes: 50 additions & 12 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ import type { PaymentResult } from '../../src/types/PaymentResult';
import { useEffect, useState } from 'react';

import PaymentConsentCreator from './util/PaymentConsentCreator';
import {
DEFAULT_LANG_OPTION,
SUPPORTED_LANG_OPTIONS,
} from './util/supportedLanguages';

type Environment = 'staging' | 'demo' | 'preview';
type PaymentMode = 'oneOff' | 'recurring' | 'recurringAndPayment';
Expand Down Expand Up @@ -63,6 +67,7 @@ function Main() {
const { showActionSheetWithOptions } = useActionSheet();
const [loading, setLoading] = useState(false);
const [paymentMode, setPaymentMode] = useState<PaymentMode>('oneOff');
const [selectedLang, setSelectedLang] = useState(DEFAULT_LANG_OPTION);
const [environment, setEnvironment] = useState<Environment>(DEFAULT_ENV);
const [paymentService, setPaymentService] = useState(
new PaymentService(DEFAULT_ENV, '', '')
Expand Down Expand Up @@ -131,29 +136,51 @@ function Main() {
);
};

const showLangSheet = () => {
const labels = [...SUPPORTED_LANG_OPTIONS, 'Cancel'];
showActionSheetWithOptions(
{
title: 'Language',
options: labels,
cancelButtonIndex: labels.length - 1,
},
(selectedIndex) => {
if (selectedIndex == null || selectedIndex === labels.length - 1)
return;
setSelectedLang(SUPPORTED_LANG_OPTIONS[selectedIndex]!);
}
);
};

const paymentModeLabel =
PAYMENT_MODE_OPTIONS.find((o) => o.value === paymentMode)?.label ??
'Payment Mode';

async function fetchSession(requireCustomerId = false) {
setLoading(true);
const lang =
selectedLang === DEFAULT_LANG_OPTION ? undefined : selectedLang;
try {
let session;
switch (paymentMode) {
case 'oneOff':
session = await SessionCreator.createOneOffSession(
paymentService,
requireCustomerId
requireCustomerId,
lang
);
break;
case 'recurring':
session = await SessionCreator.createRecurringSession(paymentService);
session = await SessionCreator.createRecurringSession(
paymentService,
lang
);
break;
case 'recurringAndPayment':
session =
await SessionCreator.createRecurringWithIntentSession(
paymentService
);
session = await SessionCreator.createRecurringWithIntentSession(
paymentService,
lang
);
break;
default:
throw new Error('Unknown payment mode');
Expand Down Expand Up @@ -279,9 +306,14 @@ function Main() {
style={styles.loading}
/>
)}
<TouchableOpacity style={styles.picker} onPress={showPaymentModeSheet}>
<Text style={styles.pickerText}>{paymentModeLabel}</Text>
</TouchableOpacity>
<View style={styles.pickerRow}>
<TouchableOpacity style={styles.picker} onPress={showPaymentModeSheet}>
<Text style={styles.pickerText}>{paymentModeLabel}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.picker} onPress={showLangSheet}>
<Text style={styles.pickerText}>lang: {selectedLang}</Text>
</TouchableOpacity>
</View>

<TouchableOpacity
style={styles.button}
Expand Down Expand Up @@ -395,15 +427,21 @@ const styles = StyleSheet.create({
left: '50%',
transform: [{ translateX: -25 }, { translateY: -25 }],
},
pickerRow: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
width: '80%',
marginBottom: 20,
gap: 16,
},
picker: {
flex: 1,
paddingVertical: 12,
paddingHorizontal: 10,
borderWidth: 1,
borderColor: 'gray',
borderRadius: 4,
width: '80%',
alignSelf: 'center',
marginBottom: 20,
alignItems: 'center',
},
pickerText: {
Expand Down
12 changes: 9 additions & 3 deletions example/src/util/SessionCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import { getCustomerParams, getPaymentParams } from '../api/PaymentParams';
class SessionCreator {
static async createOneOffSession(
paymentService: PaymentService,
requireCustomerId: boolean = false
requireCustomerId: boolean = false,
lang?: string
): Promise<PaymentSession> {
try {
let customerId = null;
Expand Down Expand Up @@ -110,6 +111,7 @@ class SessionCreator {
autoCapture: true,
hidePaymentConsents: false,
clientSecret: clientSecret,
lang,
};
return session;
} catch (error) {
Expand All @@ -119,7 +121,8 @@ class SessionCreator {
}

static async createRecurringSession(
paymentService: PaymentService
paymentService: PaymentService,
lang?: string
): Promise<PaymentSession> {
try {
const customerInfo =
Expand Down Expand Up @@ -150,6 +153,7 @@ class SessionCreator {
returnUrl: '',
nextTriggeredBy: NextTriggeredBy.Merchant,
merchantTriggerReason: MerchantTriggerReason.Scheduled,
lang,
};
return session;
} catch (error) {
Expand All @@ -159,7 +163,8 @@ class SessionCreator {
}

static async createRecurringWithIntentSession(
paymentService: PaymentService
paymentService: PaymentService,
lang?: string
): Promise<PaymentSession> {
try {
const customerInfo =
Expand Down Expand Up @@ -205,6 +210,7 @@ class SessionCreator {
returnUrl: '',
nextTriggeredBy: NextTriggeredBy.Merchant,
merchantTriggerReason: MerchantTriggerReason.Scheduled,
lang,
};
return session;
} catch (error) {
Expand Down
18 changes: 18 additions & 0 deletions example/src/util/supportedLanguages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const DEFAULT_LANG_OPTION = 'default';

/** Lang values for PaymentSession.lang, matching iOS/Android SDK localizations. */
export const SUPPORTED_LANG_OPTIONS = [
DEFAULT_LANG_OPTION,
'de',
'en',
'es',
'fr',
'ja',
'ko',
'pt-BR',
'pt-PT',
'ru',
'th',
'zh-Hans',
'zh-Hant',
];
3 changes: 3 additions & 0 deletions ios/AirwallexSdk+Session.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ private extension AWXSession {
}
}
countryCode = params["countryCode"] as! String
if let lang = params["lang"] as? String {
self.lang = lang
}
if let returnUrl = params["returnUrl"] as? String {
self.returnURL = returnUrl
}
Expand Down
18 changes: 17 additions & 1 deletion src/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,17 @@
it.todo('write a test');
import type { OneOffSession } from '../types/PaymentSession';

describe('PaymentSession', () => {
it('includes lang when provided', () => {
const session: OneOffSession = {
type: 'OneOff',
clientSecret: 'testSecret',
currency: 'HKD',
countryCode: 'HK',
lang: 'zh-Hans',
amount: 50,
paymentIntentId: 'intent123',
};

expect(session.lang).toBe('zh-Hans');
});
});
2 changes: 2 additions & 0 deletions src/types/PaymentSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ interface BaseSession {
isEmailRequired?: boolean;
currency: string;
countryCode: string;
/** Payment UI locale (e.g. `zh-Hans`, `pt-BR`). Omit for SDK default. */
lang?: string;
amount: number;
returnUrl?: string;
googlePayOptions?: GooglePayOptions;
Expand Down
Loading