@rnw-community/react-native-payments
Version:
Accept Payments with Apple Pay and Android Pay using the Payment Request API.
127 lines (104 loc) • 6.6 kB
Markdown
# Migrating from `react-native-payments` (upstream)
This package started as a rewrite of [naoufal/react-native-payments](https://github.com/naoufal/react-native-payments),
the original — now unmaintained — library. The [readme](../../readme.md) summarizes the rewrite; this page maps
the concrete API surface so an existing integration can be ported.
## API mapping
| Upstream (`react-native-payments`) | This package (`@rnw-community/react-native-payments`) | Notes |
| --- | --- | --- |
| `npm install react-native-payments` + `react-native link` | `yarn add @rnw-community/react-native-payments` | Autolinked TurboModule — no manual linking step. See [getting-started/install.md](../getting-started/install.md). |
| `global.PaymentRequest = require('react-native-payments').PaymentRequest;` | `import { PaymentRequest } from '@rnw-community/react-native-payments';` | No global polyfill; import the class where you use it. |
| `supportedMethods: ['apple-pay']` / `['android-pay']` (string) | `supportedMethods: PaymentMethodNameEnum.ApplePay` / `PaymentMethodNameEnum.AndroidPay` (enum) | Same runtime values, typed. |
| `new PaymentRequest(methodData, details, options)` — 3rd arg `options.requestPayerName` etc. | `new PaymentRequest(methodData, details)` — payer/shipping flags live on each entry's `methodData.data` | No top-level `options` object. See [api/payment-request.md](../api/payment-request.md). |
| `paymentRequest.show()`, reusable after a rejection | `paymentRequest.show()` — **single-use**, `closed` once it settles | See [architecture.md](../architecture.md) and [migrate-from-v2.md](./migrate-from-v2.md). |
| `paymentRequest.abort()` | `paymentRequest.abort()` | Same name, TurboModule-backed, spec-mapped `DOMException` on misuse. |
| `addEventListener('shippingaddresschange' \| 'shippingoptionchange', e => e.updateWith(...))` | Same two, plus `paymentmethodchange` and the PassKit-only `couponcodechange` | Request-scoped native events, `isAnswered`, field-level errors. See [change-events.md](./change-events.md). |
| `paymentResponse.details.paymentData` / `transactionIdentifier` (iOS) | `paymentResponse.details.applePayToken` (`IosPKToken`) | One typed token object instead of loose fields. |
| `paymentResponse.details.getPaymentToken()` (Android, async) / `.paymentToken` (gateway) | `paymentResponse.details.androidPayToken` (`AndroidPaymentMethodToken`) | Synchronous typed field; no async indirection, no built-in gateway token. |
| `paymentResponse.complete('success' \| 'fail' \| 'unknown')` (string) | `paymentResponse.complete(PaymentComplete.SUCCESS \| PaymentComplete.FAIL \| PaymentComplete.UNKNOWN)` (enum) | Same three outcomes, typed — see [api/payment-complete-enum.md](../api/payment-complete-enum.md). |
| Built-in Stripe / Braintree add-on packages | Removed — bring your own gateway via `gatewayConfig` (Android) or the raw token (iOS) | Stripe/Braintree already ship their own maintained RN SDKs. |
| Untyped JS, legacy bridge module | Full TypeScript, TurboModule (New Architecture-ready) | See [architecture.md](../architecture.md). |
| Ad-hoc thrown errors, no stable identity | Spec-mapped `ConstructorError` / `DOMException` / `PaymentsError` | See [errors.md](./errors.md). |
## Worked example
Before (upstream, from the original readme's Apple Pay quickstart):
```js
// index.ios.js
global.PaymentRequest = require('react-native-payments').PaymentRequest;
const METHOD_DATA = [
{
supportedMethods: ['apple-pay'],
data: {
merchantIdentifier: 'merchant.com.your-app.namespace',
supportedNetworks: ['visa', 'mastercard', 'amex'],
countryCode: 'US',
currencyCode: 'USD',
},
},
];
const DETAILS = {
id: 'basic-example',
displayItems: [{ label: 'Movie Ticket', amount: { currency: 'USD', value: '15.00' } }],
total: { label: 'Merchant Name', amount: { currency: 'USD', value: '15.00' } },
};
const paymentRequest = new PaymentRequest(METHOD_DATA, DETAILS);
paymentRequest.show().then(paymentResponse => {
const { transactionIdentifier, paymentData } = paymentResponse.details;
return fetch('...', { method: 'POST', body: { transactionIdentifier, paymentData } })
.then(res => res.json())
.then(successHandler)
.catch(errorHandler)
.then(() => paymentResponse.complete('success'));
});
```
After (this package):
```ts
import {
PaymentComplete,
PaymentMethodNameEnum,
PaymentRequest,
SupportedNetworkEnum,
} from '@rnw-community/react-native-payments';
const methodData = [
{
supportedMethods: PaymentMethodNameEnum.ApplePay,
data: {
merchantIdentifier: 'merchant.com.your-app.namespace',
supportedNetworks: [SupportedNetworkEnum.Visa, SupportedNetworkEnum.Mastercard, SupportedNetworkEnum.Amex],
countryCode: 'US',
currencyCode: 'USD',
},
},
];
const paymentDetails = {
id: 'basic-example',
displayItems: [{ label: 'Movie Ticket', amount: { currency: 'USD', value: '15.00' } }],
total: { label: 'Merchant Name', amount: { currency: 'USD', value: '15.00' } },
};
const paymentRequest = new PaymentRequest(methodData, paymentDetails);
const paymentResponse = await paymentRequest.show();
const applePayToken = paymentResponse.details.applePayToken; // typed IosPKToken
let responseBody: unknown;
let backendAccepted = false;
let backendError: unknown;
try {
const result = await fetch('...', { method: 'POST', body: JSON.stringify(applePayToken) });
if (!result.ok) {
throw new Error(`Backend rejected the payment: ${result.status}`);
}
responseBody = await result.json();
backendAccepted = true;
} catch (error) {
backendError = error;
}
// complete() is called exactly once, before either handler, so a throw from successHandler/errorHandler can
// never prevent it from running or trigger a second call.
await paymentResponse.complete(backendAccepted ? PaymentComplete.SUCCESS : PaymentComplete.FAIL);
if (backendAccepted) {
successHandler(responseBody);
} else {
errorHandler(backendError);
}
```
The differences that matter beyond syntax: no `global.PaymentRequest` polyfill, enums instead of string literals,
one typed `applePayToken` instead of loose `transactionIdentifier`/`paymentData` fields, and — most importantly —
this `paymentRequest` cannot `show()` again. A retry constructs a new `PaymentRequest` from the same
`methodData` / `paymentDetails`, matching [migrate-from-v2.md](./migrate-from-v2.md) above.