@spring-payments/payments-react-native 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +63 -31
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # Spring Payments for React Native / Expo
2
2
 
3
- A React Native library: a ready-made **Add debit or credit card** button opens native card entry. Partners do not initialize Stripe, handle intents, or build card fields. Card policy, customer authorization, saved accounts, removals, charging and duplicate-card decisions remain on Spring/partner backends.
3
+ Add an **Add debit or credit card** button to your app. It opens a native card-entry form and handles any verification the customer's bank requires. Spring manages the Stripe integration; you do not build card fields or call Stripe yourself.
4
4
 
5
- Target: Expo SDK 57, RN 0.86, Stripe React Native 0.64.x. Type checks and mocked contract tests are not certification of native 3DS behavior. Use a development build, not Expo Go, as the release verification environment. iOS/Android device tests remain a release gate. This package does not support React Native Web; use `@spring-payments/payments-js` on web.
5
+ Your app controls its design and saved-card list. Your backend checks who can use each account and starts payments through Spring. **Saving a card does not charge it.**
6
+
7
+ Supported versions: Expo SDK 57, React Native 0.86 and Stripe React Native 0.64.x. For browser apps, including React Native Web, use `@spring-payments/payments-js` instead. Test real bank verification on your supported iOS/Android devices before going live; passing type checks or simulator UI tests is not enough.
6
8
 
7
9
  Expo SDK 57 requires Node.js 22.13+ and Xcode 26.4+ for local iOS builds ([Expo requirements](https://docs.expo.dev/versions/latest/)). Older Swift compilers reject Expo's `weak let` declarations; upgrade the toolchain rather than patching Expo's Swift sources.
8
10
 
@@ -11,7 +13,7 @@ With Xcode 27/iOS 27, SDK 57 apps must also opt into Expo's scene lifecycle: ins
11
13
  `["expo-build-properties", { "ios": { "enableSceneSupport": true } }]` to your Expo
12
14
  plugins. This is app-level Expo setup, not a Spring theme option.
13
15
 
14
- ## Install
16
+ ## Install in an Expo app
15
17
 
16
18
  For Expo SDK 57 / React Native 0.86:
17
19
 
@@ -19,9 +21,9 @@ For Expo SDK 57 / React Native 0.86:
19
21
  npx expo install @spring-payments/payments-react-native @stripe/stripe-react-native@0.64.0
20
22
  ```
21
23
 
22
- For a local pre-release build, run `npm ci && npm pack` in the package source directory, then install the resulting tarball with `npx expo install /path/to/spring-payments-payments-react-native-0.1.0.tgz @stripe/stripe-react-native@0.64.0`. Registry consumers do not need access to Spring's private repository. Use the compatible Stripe version above rather than assuming an arbitrary latest version is supported.
24
+ Install from npm; do not copy the source, install a supplied archive or load a module from Gateway. No access to Spring's repository is needed. Install the Stripe version shown above because the Spring package relies on its native code, even though your app does not call Stripe directly.
23
25
 
24
- Configure your dedicated return scheme, then create/rebuild your development app:
26
+ Add the Spring plugin to your Expo configuration. **`partnerpayments` is an example, not a required Spring value.** Replace it with a scheme reserved for your app's payment callbacks, such as `acmepayments`:
25
27
 
26
28
  ```json
27
29
  {
@@ -31,18 +33,42 @@ Configure your dedicated return scheme, then create/rebuild your development app
31
33
  }
32
34
  ```
33
35
 
34
- The plugin preserves existing schemes and adds this one. Native Stripe code is autolinked; wallets are deliberately not enabled. No Apple merchant ID is needed for card-only entry. Run `npx expo run:ios`, `npx expo run:android`, or your existing EAS development-build workflow. Installing JS or an OTA update alone cannot add the native dependency.
36
+ The plugin registers the scheme without removing existing ones. Run `npx expo run:ios`, `npx expo run:android` or your EAS development-build workflow. Rebuilding is required: installing JavaScript or sending an over-the-air update cannot add native code. Use a development build, not Expo Go, for release testing. This card-entry flow does not enable wallets or require an Apple merchant ID.
37
+
38
+ ## Install in a React Native app without Expo
39
+
40
+ ```sh
41
+ npm install @spring-payments/payments-react-native @stripe/stripe-react-native@0.64.0
42
+ ```
43
+
44
+ Run your project's iOS CocoaPods installation, register the payment callback scheme in your iOS/Android configuration and rebuild the apps. The Spring SDK does not require Expo. For either installation method, do not add another `StripeProvider` or initialize Stripe separately while a Spring flow is active.
45
+
46
+ ## Before you add the UI
47
+
48
+ Your customer must be registered with Spring, have accepted the required documents and be allowed to add cards. Ask Spring to register your partner, application ID and exact return URL. Sessions are not issued for unregistered destinations.
35
49
 
36
- For bare React Native, install the package and compatible Stripe dependency normally, run your iOS CocoaPods installation, and register the dedicated URL scheme with your app's iOS/Android configuration. No Expo runtime dependency is required by this SDK. Do not add another StripeProvider or initialize Stripe separately while a Spring flow is active.
50
+ Use `<your-app-scheme>://safepay` for that URL. Replace `<your-app-scheme>` with your chosen scheme; **keep `://safepay` exactly**, with no trailing slash, extra path, query string or fragment. This address opens your native app after bank verification. It is not a website you need to host.
51
+
52
+ For example, if you choose `acmepayments`, use these matching values:
53
+
54
+ | Where | Value |
55
+ | --- | --- |
56
+ | Expo plugin `scheme`, or iOS/Android URL scheme registration | `acmepayments` |
57
+ | Spring SDK `returnUrl` | `acmepayments://safepay` |
58
+ | Exact return URL registered with Spring for your application ID | `acmepayments://safepay` |
59
+
60
+ Your backend needs Spring API credentials. Keep them on the server, never in the app. The component calls your `createSession` function, which asks your backend for a **session**: short-lived permission to add a card or verify one existing payment. Your backend checks the signed-in customer's access before requesting that session from Spring.
37
61
 
38
62
  ## One provider and one button
39
63
 
64
+ `authenticatedPartnerApi`, `refreshSavedCards` and `checkStatusWithYourBackend` below represent code you provide in your app. The SDK does not supply those functions.
65
+
40
66
  ```tsx
41
- import { SpringPaymentsProvider, SpringAddCardButton } from '@spring-payments/payments-react-native';
67
+ import { SpringPaymentsProvider, SpringAddCardButton, useSpringPayments } from '@spring-payments/payments-react-native';
42
68
 
43
69
  const options = {
44
70
  applicationId: 'com.partner.app',
45
- returnUrl: 'partnerpayments://safepay',
71
+ returnUrl: 'partnerpayments://safepay', // Replace partnerpayments with your registered scheme.
46
72
  gatewayOrigin: 'https://dev.gateway.pay-api.springpayments.com',
47
73
  merchantDisplayName: 'Your Business',
48
74
  createSession: request => authenticatedPartnerApi.createPaymentSession(request),
@@ -53,14 +79,14 @@ export function App() {
53
79
  <SpringPaymentsProvider options={options}>
54
80
  <SpringAddCardButton onResult={result => {
55
81
  if (result.status === 'COMPLETED') refreshSavedCards();
56
- if (result.status === 'PENDING') reconcileWithPartnerBackend(result);
82
+ if (result.status === 'PENDING') checkStatusWithYourBackend(result);
57
83
  }} />
58
84
  </SpringPaymentsProvider>
59
85
  );
60
86
  }
61
87
  ```
62
88
 
63
- Mount one provider above navigation. Exact web/native layouts differ; entry point, purpose and completion semantics are consistent.
89
+ Mount one provider above your app's navigation so it stays mounted while the card form is open. The web and mobile forms look different, but both report success only after Spring confirms the result.
64
90
 
65
91
  ## Styling: no Spring theme by default
66
92
 
@@ -106,40 +132,46 @@ const themedOptions = {
106
132
  };
107
133
  ```
108
134
 
109
- We pass these settings to Stripe without imposing a Spring theme. They do not allow arbitrary native-sheet layout, field order or replacement card inputs. Bank-owned 3DS challenge content is not freely themeable. Web Appearance API rules and native PaymentSheet appearance are different schemas, not an interchangeable cross-platform theme object.
135
+ Spring forwards these settings to Stripe without adding a Spring theme. You cannot freely rearrange the form, change the field order or replace secure card inputs. The bank controls its verification screen (often called **3DS**), so you cannot freely redesign that screen either. Web and native Stripe appearance settings have different fields; do not pass the same theme object to both SDKs.
110
136
 
111
- For your own button, call `useSpringPayments().addCard()`. To recover an existing off-session payment call `useSpringPayments().authenticatePayment(paymentId)`. Both return safe results, and the hook exposes `busy` and `result`. Do not create a new charge or save a replacement card for authentication recovery.
137
+ For your own button, call `useSpringPayments().addCard()`. To finish verification for an existing payment inside your app, call `useSpringPayments().authenticatePayment(paymentId)`. The hook also provides `busy` and `result`. Do not create a new charge or ask the customer to save another card just to finish verification.
112
138
 
113
- ## Backend session adapter
139
+ ## Connect your backend
114
140
 
115
- `createSession` receives `{purpose,applicationId,returnUrl,paymentId?}` and returns `{sessionId,sessionToken,expiresAt,componentUrl}`. Your authenticated backend resolves the customer and authorizes the operation, then uses Spring's M2M API:
141
+ `createSession` receives `{purpose, applicationId, returnUrl, paymentId?}`. Call your own authenticated backend with that request. Your backend checks the customer and payment, then calls Spring using its server-side API access token:
116
142
 
117
143
  - Add card: `POST /api/external/spg/v1/clients/{key}/native-card-sessions`
118
144
  - Authenticate: `POST /api/external/spg/v1/clients/{key}/payments/{paymentId}/native-authentication-sessions`
119
- - Both take `{applicationId,returnUrl}` and `Idempotency-Key`.
145
+ - Both take `{applicationId,returnUrl}` in the body and an `Idempotency-Key` header.
120
146
 
121
- Spring must register the partner UUID, application ID and exact return URL before native sessions can be issued. The app never holds Spring M2M credentials. `gatewayOrigin` restricts where the SDK sends the capability. The application-ID header identifies the session audience, not proof of app authenticity.
147
+ Return Spring's `{sessionId, sessionToken, expiresAt, componentUrl}` response to the component. Keep these credentials in memory only, not in app storage, URLs, logs or analytics. `gatewayOrigin` limits where the SDK sends the session token. An application ID does not prove who is using the app; your backend must still check the signed-in user.
122
148
 
123
- Keep an operation's idempotency key on the partner side and reuse it after network uncertainty or returning to the app; do not silently mint a fresh setup on each retry. A replay first checks Spring status, so a completed setup does not present another sheet. For an expired add-card session, reconcile the saved-account list before authorizing a new operation. Payment recovery additionally has the existing payment-status API.
149
+ An **idempotency key** identifies one attempt. Store it on your backend and reuse it when retrying a lost response or returning to the app. This lets Spring return the existing action rather than start another one. Before replacing an expired add-card session, check the saved-account list to see whether the card was already saved. For a payment, check its status before offering another attempt.
124
150
 
125
- ## Results and lifecycle
151
+ ## Handle results and app restarts
126
152
 
127
153
  - **COMPLETED**: Spring confirmed the saved account or original payment. Add-card includes `paymentAccountId`; authentication includes `paymentId`. A native sheet's success alone is not completion.
128
- - **PENDING**: The operation is unresolved, with a safe `sessionId` and, for payment authentication, `paymentId`. Bounded polling stops after 10 reads; check/replay the same operation through your backend rather than starting a duplicate.
129
- - **FAILED**: Safe `UNAVAILABLE`, `EXPIRED`, or `REJECTED` code; no raw Stripe or transport errors.
130
- - **CANCELLED** is reserved for cancellation known to occur before submission. The current native sheet does not expose whether the user tapped Save before dismissing, so dismissal conservatively reconciles and returns COMPLETED or PENDING, **not a claim that no card was saved**. Web can distinguish closing before its explicit submission button.
154
+ - **PENDING**: Spring has not confirmed the outcome after checking up to 10 times. The result includes `sessionId` and, for payment verification, `paymentId`. Ask your backend to check the same action; do not start a duplicate.
155
+ - **FAILED**: The flow could not continue. The code is `UNAVAILABLE`, `EXPIRED` or `REJECTED`; raw Stripe errors and credentials are not returned.
156
+ - **CANCELLED** means the SDK knows nothing was submitted. Closing the native form does not establish that: the customer may already have pressed Save. The SDK checks with Spring and can return `COMPLETED` or `PENDING` instead. Do not tell the customer that nothing was saved simply because they closed the form.
157
+
158
+ Only one card or verification form can be open at a time. Repeated calls for the same action share its result; a different action returns `UNAVAILABLE` while the first is active. Do not switch customers or change the provider's session settings while a form is open.
159
+
160
+ Use `<your-scheme>://safepay` as the payment return URL; the `safepay` suffix is required by the iOS integration. The SDK handles the iOS link callback and Stripe handles the native Android return. Keep this route separate from your normal navigation and analytics, and never log its query parameters.
161
+
162
+ If the operating system closes your app, its in-memory session is lost. Restore the customer's sign-in, check the original action through your backend and call `createSession` again only as needed. Opening a return link does not prove success. Your app is responsible for getting the customer's consent to future charges and asking them to return when a payment needs verification.
131
163
 
132
- Only one native operation may present at a time. Duplicate calls for the same operation share the underlying work; a competing operation gets UNAVAILABLE. Do not change the provider's customer/session context or sign in as another customer while a sheet is open.
164
+ ## Spring-hosted payment recovery (preferred)
133
165
 
134
- The dedicated return URL must be `<your-scheme>://safepay` in this SDK version: direct iOS confirmation uses Stripe's fixed suffix. The provider forwards matching iOS Linking callbacks internally. Android retains Stripe's native return handling; no JS Linking event is required for success. Configure partner navigation/analytics to ignore this dedicated callback route and never log its query parameters. Do not store capabilities, processor client secrets or callback URLs in persistent storage.
166
+ For a customer returning to finish an existing payment, Spring's hosted browser recovery page is the preferred option. It opens a Spring page; it does not load a component into your app. Use this npm package for card entry and payment verification inside your app.
135
167
 
136
- After process death there is no retained JS promise/session. Restore partner login, reconcile/replay the operation through `createSession`, and let Spring determine completion; URL arrival is not payment success. Partners remain responsible for consent to future off-session charges and prompting absent customers to return.
168
+ Open a page on your website where the customer can sign in. Your backend checks that they own the original payment and creates a **web** authentication session using the Gateway origin and `{gatewayOrigin}/payment-components/recover` as the return URL. Your page submits `sessionId` and `sessionToken` to Spring in a browser form POST body. Never put session credentials in a deep link or URL, and never send a native session to the web recovery page. Returning to the app is not proof of payment; ask your backend to check the original payment's status. If you prefer verification inside the app, use `authenticatePayment(paymentId)` as described above.
137
169
 
138
- ## Verification
170
+ ## Test before going live
139
171
 
140
- `npm test` compiles against the actual RN/Stripe types and tests backend-confirmed completion, bounded pending, unknown submission/dismissal, scope mismatch, safe errors, replay/concurrent operations and plugin behavior. `npm pack` verifies the distributable includes compiled JS/types and the Expo plugin. Before release, verify fresh Expo installation and native builds on both platforms, actual card-only PaymentSheet (no saved cards/Link/wallets), issuer 3DS success/cancellation, app switching/process death, accessibility/text scaling and real Gateway webhook timing.
172
+ Test your integration on both iOS and Android with your configured backend. Check saving a card, completing and cancelling bank verification, switching apps, restarting after the operating system closes your app, screen-reader access and larger text sizes. Confirm that the card form does not offer saved cards, Link or wallets. Check that your app handles payment notifications arriving later than the form's result.
141
173
 
142
- Version 0.1.0 is an initial integration release, not certification of a live payment flow. iOS simulator Release build and styled/unstyled UI checks have passed; actual PaymentSheet/3DS with a configured backend and Android native verification remain outstanding. Complete those checks before a production integration.
174
+ The 0.1.x releases are initial integration releases, not certification of a live payment flow. iOS simulator Release build and styled/unstyled UI checks have passed; actual PaymentSheet/3DS with a configured backend and Android native verification remain outstanding. Complete those checks before a production integration.
143
175
 
144
176
  ## License
145
177
 
@@ -147,7 +179,7 @@ MIT; see [LICENSE](./LICENSE). This license covers the Spring code in this SDK p
147
179
 
148
180
  ## Maintaining and publishing (Spring maintainers)
149
181
 
150
- Source of truth: `packages/spring-payments-react-native/` in the private `ktechgit/spring-payments` repository. Edit `src/`, `app.plugin.js` and this README here. `dist/` is generated; do not edit it. The repository's `examples/embedded-partner-expo/` is the integration consumer, not the package source.
182
+ Source of truth: `packages/spring-payments-react-native/` in the private `ktechgit/spring-payments` repository. Edit `src/`, `app.plugin.js` and this README here. `dist/` is generated; do not edit it. The repository's `examples/embedded-partner-expo/` is the integration consumer, not the package source. Local tarballs below are for maintainer release validation only, not an alternative partner installation route.
151
183
 
152
184
  From this directory:
153
185
 
@@ -165,8 +197,8 @@ For subsequent releases, choose a semantic version and run `npm version patch --
165
197
  Authenticate interactively with `npm login --registry=https://registry.npmjs.org/`; confirm the account with `npm whoami` and its publish access to `@spring-payments`. Keep npm credentials in the maintainer/build environment only. With release authorization, publish the exact inspected tarball (replace the version in its filename):
166
198
 
167
199
  ```sh
168
- npm publish ./spring-payments-payments-react-native-0.1.0.tgz --access public --registry=https://registry.npmjs.org/
169
- npm view @spring-payments/payments-react-native@0.1.0 version license dist.integrity --registry=https://registry.npmjs.org/
200
+ npm publish ./spring-payments-payments-react-native-0.1.2.tgz --access public --registry=https://registry.npmjs.org/
201
+ npm view @spring-payments/payments-react-native@0.1.2 version license dist.integrity --registry=https://registry.npmjs.org/
170
202
  ```
171
203
 
172
204
  For a preview release, use a prerelease version and `--tag next`; do not move `latest` inadvertently. After registry verification, install the published version in the Expo demo, regenerate its lockfile, run its typecheck and Expo configuration check, and update partner installation instructions. An npm release does not deploy Gateway configuration, authorize a native application or complete device/processor certification.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spring-payments/payments-react-native",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "license": "MIT",
5
5
  "description": "Spring-managed native card saving and payment authentication",
6
6
  "publishConfig": {