Skip to content

Commit 65d61a8

Browse files
committed
feat: add support for Billie
1 parent 0c23170 commit 65d61a8

10 files changed

Lines changed: 224 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## x.x.x - x-x-x
44

5+
**Features**
6+
- [#1956](https://github.com/stripe/stripe-react-native/pull/1956) Feat: Add support for Billie
7+
58
**Fixes**
69
- Fixed iOS crash "Stripe SDK confirmPayment must not be null" when calling `confirmPayment` with optional parameters. Updated native bridge to properly handle nullable params argument. [#2048](https://github.com/stripe/stripe-react-native/issues/2048)
710

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Get started with our [📚 integration guides](https://stripe.com/docs/payments/
2323

2424
**Native UI**: We provide native screens and elements to securely collect payment details on Android and iOS.
2525

26-
**PaymentSheet**: [Learn how to integrate](https://stripe.com/docs/payments/accept-a-payment) PaymentSheet, our new pre-built payments UI for mobile apps. PaymentSheet lets you accept cards, Apple Pay, Google Pay, and much more out of the box and also supports saving & reusing payment methods. PaymentSheet currently accepts the following payment methods: Card, Apple Pay, Google Pay, SEPA Debit, Bancontact, iDEAL, EPS, P24, Afterpay/Clearpay, Klarna, Giropay, and ACH.
26+
**PaymentSheet**: [Learn how to integrate](https://stripe.com/docs/payments/accept-a-payment) PaymentSheet, our new pre-built payments UI for mobile apps. PaymentSheet lets you accept cards, Apple Pay, Google Pay, and much more out of the box and also supports saving & reusing payment methods. PaymentSheet currently accepts the following payment methods: Card, Apple Pay, Google Pay, SEPA Debit, Bancontact, Billie, iDEAL, EPS, P24, Afterpay/Clearpay, Klarna, Giropay, and ACH.
2727

2828
#### Recommended usage
2929

android/src/main/java/com/reactnativestripesdk/PaymentMethodCreateParamsFactory.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class PaymentMethodCreateParamsFactory(
3939
PaymentMethod.Type.Ideal -> createIDEALParams()
4040
PaymentMethod.Type.Alipay -> createAlipayParams()
4141
PaymentMethod.Type.Bancontact -> createBancontactParams()
42+
PaymentMethod.Type.Billie -> createBillieParams()
4243
PaymentMethod.Type.SepaDebit -> createSepaParams()
4344
PaymentMethod.Type.Oxxo -> createOXXOParams()
4445
PaymentMethod.Type.Giropay -> createGiropayParams()
@@ -90,6 +91,14 @@ class PaymentMethodCreateParamsFactory(
9091
throw PaymentMethodCreateParamsException("You must provide billing details")
9192
}
9293

94+
@Throws(PaymentMethodCreateParamsException::class)
95+
private fun createBillieParams(): PaymentMethodCreateParams {
96+
return PaymentMethodCreateParams.createBillie(
97+
billingDetails = billingDetailsParams,
98+
metadata = metadataParams,
99+
)
100+
}
101+
93102
@Throws(PaymentMethodCreateParamsException::class)
94103
private fun createSepaParams(): PaymentMethodCreateParams {
95104
billingDetailsParams?.let {
@@ -256,6 +265,7 @@ class PaymentMethodCreateParamsFactory(
256265
PaymentMethod.Type.Ideal,
257266
PaymentMethod.Type.Alipay,
258267
PaymentMethod.Type.Bancontact,
268+
PaymentMethod.Type.Billie,
259269
PaymentMethod.Type.SepaDebit,
260270
PaymentMethod.Type.Oxxo,
261271
PaymentMethod.Type.Giropay,

android/src/main/java/com/reactnativestripesdk/utils/Mappers.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ internal fun mapPaymentMethodType(type: PaymentMethod.Type?): String =
129129
PaymentMethod.Type.AuBecsDebit -> "AuBecsDebit"
130130
PaymentMethod.Type.BacsDebit -> "BacsDebit"
131131
PaymentMethod.Type.Bancontact -> "Bancontact"
132+
PaymentMethod.Type.Billie -> "Billie"
132133
PaymentMethod.Type.Card -> "Card"
133134
PaymentMethod.Type.CardPresent -> "CardPresent"
134135
PaymentMethod.Type.Eps -> "Eps"
@@ -159,6 +160,7 @@ internal fun mapToPaymentMethodType(type: String?): PaymentMethod.Type? =
159160
"AuBecsDebit" -> PaymentMethod.Type.AuBecsDebit
160161
"BacsDebit" -> PaymentMethod.Type.BacsDebit
161162
"Bancontact" -> PaymentMethod.Type.Bancontact
163+
"Billie" -> PaymentMethod.Type.Billie
162164
"AfterpayClearpay" -> PaymentMethod.Type.AfterpayClearpay
163165
"CardPresent" -> PaymentMethod.Type.CardPresent
164166
"Eps" -> PaymentMethod.Type.Eps

example/src/App.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import AlipayPaymentScreen from './screens/AlipayPaymentScreen';
2020
import PaymentResultScreen from './screens/PaymentResultScreen';
2121
import BancontactPaymentScreen from './screens/BancontactPaymentScreen';
2222
import BancontactSetupFuturePaymentScreen from './screens/BancontactSetupFuturePaymentScreen';
23+
import BilliePaymentScreen from './screens/BilliePaymentScreen';
2324
import SepaPaymentScreen from './screens/SepaPaymentScreen';
2425
import SepaSetupFuturePaymentScreen from './screens/SepaSetupFuturePaymentScreen';
2526
import OxxoPaymentScreen from './screens/OxxoPaymentScreen';
@@ -69,6 +70,7 @@ export type RootStackParamList = {
6970
PaymentResultScreen: { url: string };
7071
BancontactPaymentScreen: undefined;
7172
BancontactSetupFuturePaymentScreen: undefined;
73+
BilliePaymentScreen: undefined;
7274
SepaPaymentScreen: undefined;
7375
SepaSetupFuturePaymentScreen: undefined;
7476
OxxoPaymentScreen: undefined;
@@ -226,6 +228,10 @@ export default function App() {
226228
name="BancontactSetupFuturePaymentScreen"
227229
component={BancontactSetupFuturePaymentScreen}
228230
/>
231+
<Stack.Screen
232+
name="BilliePaymentScreen"
233+
component={BilliePaymentScreen}
234+
/>
229235
<Stack.Screen
230236
name="SepaPaymentScreen"
231237
component={SepaPaymentScreen}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import React, { useState } from 'react';
2+
import { Alert, StyleSheet, TextInput } from 'react-native';
3+
import {
4+
useConfirmPayment,
5+
useConfirmSetupIntent,
6+
} from '@stripe/stripe-react-native';
7+
import Button from '../components/Button';
8+
import PaymentScreen from '../components/PaymentScreen';
9+
import { API_URL } from '../Config';
10+
import { colors } from '../colors';
11+
12+
export default function BilliePaymentScreen() {
13+
const [email, setEmail] = useState('');
14+
const { confirmPayment, loading } = useConfirmPayment();
15+
const { confirmSetupIntent, loading: setupLoading } = useConfirmSetupIntent();
16+
17+
const fetchClientSecret = async (intentType: 'setup' | 'payment') => {
18+
const response = await fetch(`${API_URL}/create-${intentType}-intent`, {
19+
method: 'POST',
20+
headers: {
21+
'Content-Type': 'application/json',
22+
},
23+
body: JSON.stringify({
24+
email,
25+
currency: 'eur',
26+
items: ['id-1'],
27+
payment_method_types: ['billie'],
28+
}),
29+
});
30+
const { clientSecret, error } = await response.json();
31+
32+
return { clientSecret, error };
33+
};
34+
35+
const handlePayPress = async () => {
36+
const { clientSecret, error: clientSecretError } =
37+
await fetchClientSecret('payment');
38+
39+
if (clientSecretError) {
40+
Alert.alert(`Error`, clientSecretError);
41+
return;
42+
}
43+
44+
const { error, paymentIntent } = await confirmPayment(clientSecret, {
45+
paymentMethodType: 'Billie',
46+
paymentMethodData: {
47+
billingDetails: {
48+
email: 'stripe@test.com',
49+
address: {
50+
country: 'US',
51+
},
52+
},
53+
},
54+
});
55+
56+
if (error) {
57+
Alert.alert(`Error code: ${error.code}`, error.message);
58+
console.log('Payment confirmation error', error.message);
59+
} else if (paymentIntent) {
60+
Alert.alert(
61+
'Success',
62+
`The payment was confirmed successfully! currency: ${paymentIntent.currency}`
63+
);
64+
}
65+
};
66+
67+
const handleSetupPress = async () => {
68+
const { clientSecret, error: clientSecretError } =
69+
await fetchClientSecret('setup');
70+
71+
if (clientSecretError) {
72+
Alert.alert(`Error`, clientSecretError);
73+
return;
74+
}
75+
76+
const { error, setupIntent } = await confirmSetupIntent(clientSecret, {
77+
paymentMethodType: 'Billie',
78+
paymentMethodData: {
79+
mandateData: {
80+
customerAcceptance: {
81+
online: {
82+
ipAddress: '1.1.1.1',
83+
userAgent: 'my-agent',
84+
},
85+
},
86+
},
87+
shippingDetails: {
88+
address: {
89+
city: 'Houston',
90+
country: 'US',
91+
line1: '1459 Circle Drive',
92+
state: 'Texas',
93+
postalCode: '77063',
94+
},
95+
email: 'myemail@s.com',
96+
name: 'John Doe',
97+
},
98+
billingDetails: {
99+
email: 'stripe@test.com',
100+
address: {
101+
country: 'US',
102+
},
103+
},
104+
},
105+
});
106+
107+
if (error) {
108+
Alert.alert(`Error code: ${error.code}`, error.message);
109+
console.log('Setup confirmation error', error.message);
110+
} else if (setupIntent) {
111+
Alert.alert('Success', `Status: ${setupIntent.status}`);
112+
console.log('Success from promise', setupIntent);
113+
}
114+
};
115+
116+
return (
117+
<PaymentScreen>
118+
<TextInput
119+
autoCapitalize="none"
120+
placeholder="E-mail"
121+
keyboardType="email-address"
122+
onChange={(value) => setEmail(value.nativeEvent.text)}
123+
style={styles.input}
124+
/>
125+
126+
<Button
127+
variant="primary"
128+
onPress={handlePayPress}
129+
title="Pay"
130+
accessibilityLabel="Pay"
131+
loading={loading}
132+
/>
133+
134+
<Button
135+
variant="primary"
136+
onPress={handleSetupPress}
137+
title="Setup"
138+
accessibilityLabel="Setup"
139+
loading={setupLoading}
140+
/>
141+
</PaymentScreen>
142+
);
143+
}
144+
145+
const styles = StyleSheet.create({
146+
cardField: {
147+
width: '100%',
148+
height: 50,
149+
marginVertical: 30,
150+
},
151+
row: {
152+
flexDirection: 'row',
153+
alignItems: 'center',
154+
marginBottom: 20,
155+
},
156+
text: {
157+
marginLeft: 12,
158+
},
159+
input: {
160+
height: 44,
161+
borderBottomColor: colors.slate,
162+
borderBottomWidth: 1.5,
163+
marginBottom: 20,
164+
},
165+
});

example/src/screens/HomeScreen.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,14 @@ export default function HomeScreen() {
330330
}}
331331
/>
332332
</View>
333+
<View style={styles.buttonContainer}>
334+
<Button
335+
title="Billie"
336+
onPress={() => {
337+
navigation.navigate('BilliePaymentScreen');
338+
}}
339+
/>
340+
</View>
333341
<View style={styles.buttonContainer}>
334342
<Button
335343
title="Affirm"

ios/Mappers.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ class Mappers {
284284
case STPPaymentMethodType.przelewy24: return "P24"
285285
case STPPaymentMethodType.EPS: return "Eps"
286286
case STPPaymentMethodType.bancontact: return "Bancontact"
287+
case STPPaymentMethodType.billie: return "Billie"
287288
case STPPaymentMethodType.OXXO: return "Oxxo"
288289
case STPPaymentMethodType.UPI: return "Upi"
289290
case STPPaymentMethodType.afterpayClearpay: return "AfterpayClearpay"
@@ -314,6 +315,7 @@ class Mappers {
314315
case "P24": return STPPaymentMethodType.przelewy24
315316
case "Eps": return STPPaymentMethodType.EPS
316317
case "Bancontact": return STPPaymentMethodType.bancontact
318+
case "Billie": return STPPaymentMethodType.billie
317319
case "Oxxo": return STPPaymentMethodType.OXXO
318320
case "Upi": return STPPaymentMethodType.UPI
319321
case "AfterpayClearpay": return STPPaymentMethodType.afterpayClearpay

ios/PaymentMethodFactory.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class PaymentMethodFactory {
3333
return try createAlipayPaymentMethodParams()
3434
case STPPaymentMethodType.bancontact:
3535
return try createBancontactPaymentMethodParams()
36+
case STPPaymentMethodType.billie:
37+
return try createBilliePaymentMethodParams()
3638
case STPPaymentMethodType.SEPADebit:
3739
return try createSepaPaymentMethodParams()
3840
case STPPaymentMethodType.giropay:
@@ -84,6 +86,8 @@ class PaymentMethodFactory {
8486
return try createAlipayPaymentMethodOptions()
8587
case STPPaymentMethodType.bancontact:
8688
return nil
89+
case STPPaymentMethodType.billie:
90+
return nil
8791
case STPPaymentMethodType.SEPADebit:
8892
return nil
8993
case STPPaymentMethodType.OXXO:
@@ -256,6 +260,16 @@ class PaymentMethodFactory {
256260
return STPPaymentMethodParams(bancontact: params, billingDetails: billingDetails, metadata: metadata)
257261
}
258262

263+
private func createBilliePaymentMethodParams() throws -> STPPaymentMethodParams {
264+
let params = STPPaymentMethodBillieParams()
265+
266+
if let billingDetails = billingDetailsParams {
267+
return STPPaymentMethodParams(billie: params, billingDetails: billingDetails, metadata: metadata)
268+
} else {
269+
throw PaymentMethodError.billiePaymentMissingParams
270+
}
271+
}
272+
259273
private func createSepaPaymentMethodParams() throws -> STPPaymentMethodParams {
260274
let params = STPPaymentMethodSEPADebitParams()
261275

@@ -399,6 +413,7 @@ enum PaymentMethodError: Error {
399413
case paymentNotSupported
400414
case cardPaymentOptionsMissingParams
401415
case bancontactPaymentMissingParams
416+
case billiePaymentMissingParams
402417
case sepaPaymentMissingParams
403418
case giropayPaymentMissingParams
404419
case p24PaymentMissingParams
@@ -423,6 +438,8 @@ extension PaymentMethodError: LocalizedError {
423438
return NSLocalizedString("You must provide billing details", comment: "Create payment error")
424439
case .bancontactPaymentMissingParams:
425440
return NSLocalizedString("You must provide billing details", comment: "Create payment error")
441+
case .billiePaymentMissingParams:
442+
return NSLocalizedString("Billie requires that you provide the following billing details: email, country", comment: "Create payment error")
426443
case .sepaPaymentMissingParams:
427444
return NSLocalizedString("You must provide billing details and IBAN", comment: "Create payment error")
428445
case .epsPaymentMissingParams:

src/types/PaymentIntent.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export type ConfirmParams =
4545
| KlarnaParams
4646
// | WeChatPayParams
4747
| BancontactParams
48+
| BillieParams
4849
| USBankAccountParams
4950
| PayPalParams
5051
| AffirmParams
@@ -166,6 +167,15 @@ export interface BancontactParams {
166167
};
167168
}
168169

170+
export type BillieParams = {
171+
paymentMethodType: 'Billie';
172+
paymentMethodData: {
173+
billingDetails?: BillingDetails;
174+
mandateData?: MandateData;
175+
metadata?: MetaData;
176+
};
177+
};
178+
169179
export interface SepaParams {
170180
paymentMethodType: 'SepaDebit';
171181
paymentMethodData: {

0 commit comments

Comments
 (0)