# Accept an iDEAL | Wero payment
Learn how to accept iDEAL | Wero, a common payment method in the Netherlands.
# React Native
> We recommend that you follow the [Accept a payment](https://docs.stripe.com/payments/accept-a-payment.md) guide unless you need to use manual server-side confirmation, or your integration requires presenting payment methods separately. If you’ve already integrated with Elements, see the [Payment Element migration guide](https://docs.stripe.com/payments/payment-element/migration.md).
Accepting iDEAL | Wero in your app consists of displaying a webview that sends your customer to their bank’s online portal to authorize the payment. Your customer then returns to your app and you’re [immediately notified](https://docs.stripe.com/payments/payment-methods.md#payment-notification) about whether the payment succeeded or failed.
> To accept iDEAL | Wero, you must comply with our [iDEAL Terms of Service](https://stripe.com/ideal/legal).
## Set up Stripe [Server-side] [Client-side]
### Server-side
This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:
#### Ruby
```bash
# Available as a gem
sudo gem install stripe
```
```ruby
# If you use bundler, you can add this line to your Gemfile
gem 'stripe'
```
### Client-side
The [React Native SDK](https://github.com/stripe/stripe-react-native) is open source and fully documented. Internally, it uses the [native iOS](https://github.com/stripe/stripe-ios) and [Android](https://github.com/stripe/stripe-android) SDKs. To install Stripe’s React Native SDK, run one of the following commands in your project’s directory (depending on which package manager you use):
#### yarn
```bash
yarn add @stripe/stripe-react-native
```
#### npm
```bash
npm install @stripe/stripe-react-native
```
Next, install some other necessary dependencies:
- For iOS, go to the **ios** directory and run `pod install` to ensure that you also install the required native dependencies.
- For Android, there are no more dependencies to install.
> We recommend following the [official TypeScript guide](https://reactnative.dev/docs/typescript#adding-typescript-to-an-existing-project) to add TypeScript support.
### Stripe initialization
To initialize Stripe in your React Native app, either wrap your payment screen with the `StripeProvider` component, or use the `initStripe` initialization method. Only the API [publishable key](https://docs.stripe.com/keys.md#obtain-api-keys) in `publishableKey` is required. The following example shows how to initialize Stripe using the `StripeProvider` component.
```jsx
import { useState, useEffect } from 'react';
import { StripeProvider } from '@stripe/stripe-react-native';
function App() {
const [publishableKey, setPublishableKey] = useState('');
const fetchPublishableKey = async () => {
const key = await fetchKey(); // fetch key from your server here
setPublishableKey(key);
};
useEffect(() => {
fetchPublishableKey();
}, []);
return (
{/* Your app code here */}
);
}
```
> Use your API [test keys](https://docs.stripe.com/keys.md#obtain-api-keys) while you test and develop, and your [live mode](https://docs.stripe.com/keys.md#test-live-modes) keys when you publish your app.
## Create a PaymentIntent [Server-side] [Client-side]
A [PaymentIntent](https://docs.stripe.com/api/payment_intents/object.md) is an object that represents your intent to collect payment from a customer and tracks the lifecycle of the payment process through each stage.
### Server-side
First, create a `PaymentIntent` on your server and specify the amount to collect and the `eur` currency (iDEAL | Wero doesn’t support other currencies). iDEAL | Wero doesn’t have a minimum charge amount, so the payment `amount` value can be as low as 1. If you already have an integration using the [Payment Intents API](https://docs.stripe.com/payments/payment-intents.md), add `ideal` to the list of [payment method types](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_types) for your `PaymentIntent`.
```curl
curl https://api.stripe.com/v1/payment_intents \
-u "<>:" \
-d amount=1099 \
-d currency=eur \
-d "payment_method_types[]=ideal"
```
Instead of passing the entire PaymentIntent object to your app, return its *client secret* (The client secret is a unique key returned from Stripe as part of a PaymentIntent. This key lets the client access important fields from the PaymentIntent (status, amount, currency) while hiding sensitive ones (metadata, customer)). The PaymentIntent’s client secret is a unique key that lets you *confirm* (Confirming an intent indicates that the customer intends to use the current or provided payment method. Upon confirmation, the intent attempts to initiate the portions of the flow that have real-world side effects) the payment and update payment details on the client, without allowing manipulation of sensitive information, like payment amount.
### Client-side
On the client, request a PaymentIntent from your server and store its client secret.
```javascript
const fetchPaymentIntentClientSecret = async () => {
const response = await fetch(`${API_URL}/create-payment-intent`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email,
currency: 'eur',
payment_method_types: ['ideal'],
}),
});
const {clientSecret, error} = await response.json();
return {clientSecret, error};
};
```
## Collect payment method details [Client-side]
In your app, collect your customer’s full name.
```javascript
export default function IdealPaymentScreen() {
const [name, setName] = useState();
const handlePayPress = async () => {
// ...
};
return (
setName(value.nativeEvent.text)}
/>
);
}
```
## Submit the payment to Stripe [Client-side]
Retrieve the client secret from the PaymentIntent you created and call `confirmPayment`. This presents a webview where the customer can complete the payment on their bank’s website or app. Afterwards, the promise resolves with the result of the payment.
```javascript
export default function IdealPaymentScreen() {
const [name, setName] = useState();
const handlePayPress = async () => {
const {clientSecret} = await fetchPaymentIntentClientSecret();
const {error, paymentIntent} = await confirmPayment(clientSecret, {
paymentMethodType: 'Ideal',
paymentMethodData: {
billingDetails: {
name,
},
},
});
if (error) {
Alert.alert(`Error code: ${error.code}`, error.message);
} else if (paymentIntent) {
Alert.alert(
'Success',
`The payment was confirmed successfully! currency: ${paymentIntent.currency}`,
);
}
};
return {/* ... */};
}
```
## Test your integration
Use your [test API keys](https://docs.stripe.com/keys.md#test-live-modes) to confirm the PaymentIntent. After confirming, you’re redirected to a test page with options to authorize or fail the payment.
- Click **Authorize test payment** to test the case when the payment is successful. The PaymentIntent transitions from `requires_action` to `succeeded`.
- Click **Fail test payment** to test the case when the customer fails to authenticate. The PaymentIntent transitions from `requires_action` to `requires_payment_method`.
## Optional: Handle post-payment events
Stripe sends a [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.succeeded) event when the payment completes. Use the Dashboard, a custom *webhook* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests), or a partner solution to receive these events and run actions, like sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow.
Listen for these events rather than waiting on a callback from the client. On the client, the customer could close the browser window or quit the app before the callback executes, and malicious clients could manipulate the response. Setting up your integration to listen for asynchronous events also helps you accept more payment methods in the future. Learn about the [differences between all supported payment methods](https://stripe.com/payments/payment-methods-guide).
- **Handle events manually in the Dashboard**
Use the Dashboard to [View your test payments in the Dashboard](https://dashboard.stripe.com/test/payments), send email receipts, handle payouts, or retry failed payments.
- **Build a custom webhook**
[Build a custom webhook](https://docs.stripe.com/webhooks/handling-payment-events.md#build-your-own-webhook) handler to listen for events and build custom asynchronous payment flows. Test and debug your webhook integration locally with the Stripe CLI.
- **Integrate a prebuilt app**
Handle common business events, such as [automation](https://stripe.partners/?f_category=automation) or [marketing and sales](https://stripe.partners/?f_category=marketing-and-sales), by integrating a partner application.
## Optional: Handle deep linking
When a customer exits your app (for example to authenticate in Safari or their banking app), provide a way for them to automatically return to your app. Many payment method types *require* a return URL. If you don’t provide one, we can’t present payment methods that require a return URL to your users, even if you’ve enabled them.
To provide a return URL:
1. [Register](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app#Register-your-URL-scheme) a custom URL. Universal links aren’t supported.
1. [Configure](https://reactnative.dev/docs/linking) your custom URL.
1. Set up your root component to forward the URL to the Stripe SDK as shown below.
> If you’re using Expo, [set your scheme](https://docs.expo.io/guides/linking/#in-a-standalone-app) in the `app.json` file.
```jsx
import { useEffect, useCallback } from 'react';
import { Linking, View } from 'react-native';
import { useStripe } from '@stripe/stripe-react-native';
export default function MyApp() {
const { handleURLCallback } = useStripe();
const handleDeepLink = useCallback(
async (url: string | null) => {
if (url) {
const stripeHandled = await handleURLCallback(url);
if (stripeHandled) {
// This was a Stripe URL - you can return or add extra handling here as you see fit
} else {
// This was NOT a Stripe URL – handle as you normally would
}
}
},
[handleURLCallback]
);
useEffect(() => {
const getUrlAsync = async () => {
const initialUrl = await Linking.getInitialURL();
handleDeepLink(initialUrl);
};
getUrlAsync();
const deepLinkListener = Linking.addEventListener(
'url',
(event: { url: string }) => {
handleDeepLink(event.url);
}
);
return () => deepLinkListener.remove();
}, [handleDeepLink]);
return (
);
}
```
For more information on native URL schemes, refer to the [Android](https://developer.android.com/training/app-links/deep-linking) and [iOS](https://developer.apple.com/documentation/xcode/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app) docs.
## Bank reference
| Bank name | Value |
| ----------------------- | -------------- |
| ABN AMRO | `abn_amro` |
| ASN Bank | `asn_bank` |
| Bunq | `bunq` |
| ING | `ing` |
| Knab | `knab` |
| N26 | `n26` |
| Nationale-Nederlanden | `nn` |
| Rabobank | `rabobank` |
| Revolut | `revolut` |
| RegioBank | `regiobank` |
| SNS Bank (De Volksbank) | `sns_bank` |
| Triodos Bank | `triodos_bank` |
| Van Lanschot | `van_lanschot` |
| Yoursafe | `yoursafe` |