@autra-io/react-native-card 0.1.0
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.
- package/CHANGELOG.md +52 -0
- package/LICENSE +21 -0
- package/README.md +298 -0
- package/README.pt-BR.md +133 -0
- package/SECURITY.md +61 -0
- package/dist/index.cjs +341 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +53 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +334 -0
- package/dist/index.js.map +1 -0
- package/package.json +90 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `AutraCardForm`: hosted-WebView card tokenization component. Loads only a
|
|
13
|
+
backend-created `hostedCardUrl` (hosted card session); no provider token
|
|
14
|
+
ever reaches the app. Strict postMessage schema with origin allow-list and
|
|
15
|
+
field allow-list on `tokenized` payloads.
|
|
16
|
+
- WebView hardening: HTTPS-only hosted URL, full navigation allow-list in
|
|
17
|
+
`onShouldStartLoadWithRequest` with wildcard `originWhitelist` (blocked
|
|
18
|
+
navigations never escape to the OS browser), `incognito` +
|
|
19
|
+
`cacheEnabled: false` (no disk persistence of the hosted page),
|
|
20
|
+
`mixedContentMode: 'never'`, `allowFileAccess: false`.
|
|
21
|
+
- Terminal guard: after a successful `tokenized` message, duplicate messages
|
|
22
|
+
and stray errors are ignored (no double `onSuccess`).
|
|
23
|
+
- `onHttpError` document-vs-sub-resource discrimination: sub-resource
|
|
24
|
+
failures (favicon/assets/page fetches) no longer abort the flow.
|
|
25
|
+
- `allowedOrigins` normalization: trailing slashes are canonicalized and
|
|
26
|
+
non-HTTPS entries dropped; an empty normalized list is a
|
|
27
|
+
`configuration_error`.
|
|
28
|
+
- Configuration errors are emitted from an effect (never during render).
|
|
29
|
+
- Functional mock-API React Native app test harness
|
|
30
|
+
(`test/functional-app/`).
|
|
31
|
+
- Public exports in `src/index.ts`
|
|
32
|
+
(`AutraCardForm`, `AUTRA_REACT_NATIVE_CARD_SDK_VERSION`,
|
|
33
|
+
`AutraCardTokenizedResult`, `AutraCardError`).
|
|
34
|
+
- Repository foundation: package scaffold (`package.json`, `tsconfig`, tsup,
|
|
35
|
+
Vitest, ESLint flat config, Prettier).
|
|
36
|
+
- CI workflow (Node 20 & 22) and package-smoke job.
|
|
37
|
+
- Security workflow: `npm audit` (gate), gitleaks (gate), semgrep (advisory).
|
|
38
|
+
- Dependabot (npm + github-actions), CODEOWNERS, PR template.
|
|
39
|
+
- Pre-commit configuration (pre-commit framework).
|
|
40
|
+
- GitHub Copilot review instructions.
|
|
41
|
+
- Integration documentation (`docs/platform.md`, `docs/integration/*`).
|
|
42
|
+
- Secret-safety: `.gitignore`/`.env.example` guardrails and package smoke test.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- Package prepared for public release: MIT license, `publishConfig.access:
|
|
47
|
+
public`; publishing remains manual and gated (see `docs/platform.md`).
|
|
48
|
+
|
|
49
|
+
### Notes
|
|
50
|
+
|
|
51
|
+
- No version has been published to npm yet; the entries above will ship as
|
|
52
|
+
`0.1.0`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Autra
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# @autra-io/react-native-card
|
|
2
|
+
|
|
3
|
+
React Native SDK for secure card tokenization via Autra's hosted card page.
|
|
4
|
+
**Card data never touches your app or your servers.**
|
|
5
|
+
|
|
6
|
+
[](https://github.com/autratech/autra-card-sdk-react-native/actions/workflows/ci.yml)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
<!-- Enable after the first npm publish:
|
|
9
|
+
[](https://www.npmjs.com/package/@autra-io/react-native-card)
|
|
10
|
+
-->
|
|
11
|
+
|
|
12
|
+
> 🇧🇷 [Leia este README em português](./README.pt-BR.md) · Guias completos em
|
|
13
|
+
> português no portal do desenvolvedor <!-- PLACEHOLDER: developer portal URL -->
|
|
14
|
+
|
|
15
|
+
## Table of contents
|
|
16
|
+
|
|
17
|
+
- [Features](#features)
|
|
18
|
+
- [Requirements](#requirements)
|
|
19
|
+
- [Installation](#installation)
|
|
20
|
+
- [How it works](#how-it-works)
|
|
21
|
+
- [Quickstart](#quickstart)
|
|
22
|
+
- [API reference](#api-reference)
|
|
23
|
+
- [Security & PCI](#security--pci)
|
|
24
|
+
- [Testing your integration](#testing-your-integration)
|
|
25
|
+
- [Troubleshooting](#troubleshooting)
|
|
26
|
+
- [Documentation](#documentation)
|
|
27
|
+
- [Contributing](#contributing)
|
|
28
|
+
- [Support](#support)
|
|
29
|
+
- [License](#license)
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- **Hosted-page tokenization** — the cardholder types PAN/CVV on an
|
|
34
|
+
Autra-hosted page rendered inside a WebView. Card data never transits your
|
|
35
|
+
app or your backend, which helps reduce your PCI DSS scope.
|
|
36
|
+
- **Hardened WebView out of the box** — HTTPS-only, full navigation
|
|
37
|
+
allow-list (blocked navigations never escape to the OS browser),
|
|
38
|
+
`mixedContentMode: 'never'`, incognito mode, no disk cache, no file access.
|
|
39
|
+
- **Strict message bridge** — origin allow-list and schema validation on every
|
|
40
|
+
message from the hosted page; a terminal guard makes `onSuccess` fire at
|
|
41
|
+
most once.
|
|
42
|
+
- **Typed end to end** — `AutraCardFormProps`, `AutraCardTokenizedResult` and
|
|
43
|
+
a normalized `AutraCardError` taxonomy, all in TypeScript.
|
|
44
|
+
- **Zero runtime dependencies** — the SDK adds nothing to your bundle beyond
|
|
45
|
+
its own component; `react-native-webview` is a peer dependency you control.
|
|
46
|
+
|
|
47
|
+
## Requirements
|
|
48
|
+
|
|
49
|
+
| Dependency | Version |
|
|
50
|
+
| ---------------------- | --------- |
|
|
51
|
+
| React | >= 18.0.0 |
|
|
52
|
+
| React Native | >= 0.72.0 |
|
|
53
|
+
| `react-native-webview` | >= 13.0.0 |
|
|
54
|
+
|
|
55
|
+
- `react-native-webview` is a **required peer dependency** — install it in
|
|
56
|
+
your app (see below); the SDK does not bundle it.
|
|
57
|
+
- **Expo**: works with development builds / `expo prebuild` (because
|
|
58
|
+
`react-native-webview` contains native code). Expo Go is not supported.
|
|
59
|
+
<!-- PLACEHOLDER: confirm official Expo support statement with the team -->
|
|
60
|
+
- **New Architecture**: the SDK itself has no native code; support follows
|
|
61
|
+
your installed `react-native-webview` version.
|
|
62
|
+
<!-- PLACEHOLDER: confirm New Architecture statement with the team -->
|
|
63
|
+
- Node.js >= 20 is only required to develop this repository, not to use the
|
|
64
|
+
SDK.
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm install @autra-io/react-native-card react-native-webview
|
|
70
|
+
# or
|
|
71
|
+
yarn add @autra-io/react-native-card react-native-webview
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
iOS (bare React Native):
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
cd ios && pod install
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Expo:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx expo install react-native-webview
|
|
84
|
+
npm install @autra-io/react-native-card
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## How it works
|
|
88
|
+
|
|
89
|
+
```mermaid
|
|
90
|
+
sequenceDiagram
|
|
91
|
+
participant App as Your app (SDK)
|
|
92
|
+
participant Backend as Your backend
|
|
93
|
+
participant Autra as Autra Platform API
|
|
94
|
+
participant Page as Autra hosted card page
|
|
95
|
+
|
|
96
|
+
App->>Backend: request a card session
|
|
97
|
+
Backend->>Autra: POST /v1/acquiring/card-sessions (service credentials)
|
|
98
|
+
Autra-->>Backend: hostedCardUrl (short-lived, single-use)
|
|
99
|
+
Backend-->>App: hostedCardUrl
|
|
100
|
+
App->>Page: <AutraCardForm hostedCardUrl /> loads the page in a WebView
|
|
101
|
+
Note over Page: Cardholder types PAN/CVV here —<br/>never in your app
|
|
102
|
+
Page->>Autra: tokenize (direct, over HTTPS)
|
|
103
|
+
Autra-->>Page: opaque token references
|
|
104
|
+
Page-->>App: postMessage: tokenized
|
|
105
|
+
App->>Backend: slugStoredCard (opaque) for future charges
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Card data flows only between the cardholder and Autra's hosted page — it never
|
|
109
|
+
transits your app or backend. Your systems only ever see two opaque values:
|
|
110
|
+
the short-lived `hostedCardUrl` going in, and the `slugStoredCard` /
|
|
111
|
+
`slugToken` references coming out.
|
|
112
|
+
|
|
113
|
+
## Quickstart
|
|
114
|
+
|
|
115
|
+
### Step 1 — Backend: create a hosted card session
|
|
116
|
+
|
|
117
|
+
Card sessions are created **server-side** with your Autra service credentials.
|
|
118
|
+
Never call this from the app, and never embed credentials in the app.
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
// Your backend (Node example). Sandbox base URL: https://api.sandbox.autra.io
|
|
122
|
+
// Production: see the developer portal. <!-- PLACEHOLDER: production URL -->
|
|
123
|
+
app.post('/card-sessions', async (_req, res) => {
|
|
124
|
+
const response = await fetch('https://api.sandbox.autra.io/v1/acquiring/card-sessions', {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: {/* your Autra service credentials — server-side only */},
|
|
127
|
+
});
|
|
128
|
+
const { hostedCardUrl } = await response.json();
|
|
129
|
+
// hostedCardUrl is short-lived and single-use — mint one per attempt.
|
|
130
|
+
res.json({ hostedCardUrl });
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Step 2 — App: render the form
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
import { useState } from 'react';
|
|
138
|
+
import { Button, View } from 'react-native';
|
|
139
|
+
import {
|
|
140
|
+
AutraCardForm,
|
|
141
|
+
type AutraCardTokenizedResult,
|
|
142
|
+
type AutraCardError,
|
|
143
|
+
} from '@autra-io/react-native-card';
|
|
144
|
+
|
|
145
|
+
export function AddCardScreen() {
|
|
146
|
+
const [hostedCardUrl, setHostedCardUrl] = useState<string | null>(null);
|
|
147
|
+
|
|
148
|
+
async function startCardEntry() {
|
|
149
|
+
// Ask YOUR backend for a fresh session (Step 1). URLs are single-use:
|
|
150
|
+
// request a new one for every attempt — never hardcode or reuse them.
|
|
151
|
+
const response = await fetch('https://your-backend.example.com/card-sessions', {
|
|
152
|
+
method: 'POST',
|
|
153
|
+
});
|
|
154
|
+
const body = (await response.json()) as { hostedCardUrl: string };
|
|
155
|
+
setHostedCardUrl(body.hostedCardUrl);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!hostedCardUrl) {
|
|
159
|
+
return <Button title="Add card" onPress={startCardEntry} />;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<View style={{ flex: 1 }}>
|
|
164
|
+
<AutraCardForm
|
|
165
|
+
hostedCardUrl={hostedCardUrl}
|
|
166
|
+
onReady={() => {
|
|
167
|
+
// Hosted page is interactive — hide your loading indicator.
|
|
168
|
+
}}
|
|
169
|
+
onSuccess={(result: AutraCardTokenizedResult) => {
|
|
170
|
+
// Send result.slugStoredCard to your backend and store it there.
|
|
171
|
+
// Your backend uses it later to charge the card — the app is done.
|
|
172
|
+
}}
|
|
173
|
+
onError={(error: AutraCardError) => {
|
|
174
|
+
if (error.code === 'token_expired' || error.retryable) {
|
|
175
|
+
// Sessions are short-lived: create a NEW session and retry.
|
|
176
|
+
setHostedCardUrl(null);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
// Non-retryable: surface a friendly message and report error.code.
|
|
180
|
+
}}
|
|
181
|
+
style={{ flex: 1 }}
|
|
182
|
+
/>
|
|
183
|
+
</View>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## API reference
|
|
189
|
+
|
|
190
|
+
### `<AutraCardForm />`
|
|
191
|
+
|
|
192
|
+
| Prop | Type | Required | Description |
|
|
193
|
+
| ---------------- | -------------------------------------------- | -------- | --------------------------------------------------------------------------------------------- |
|
|
194
|
+
| `hostedCardUrl` | `string` | yes | Short-lived hosted card session URL created by your backend. Must be HTTPS. |
|
|
195
|
+
| `onSuccess` | `(result: AutraCardTokenizedResult) => void` | yes | Fires **at most once**, on successful tokenization. Later messages are ignored. |
|
|
196
|
+
| `onError` | `(error: AutraCardError) => void` | yes | Fires on configuration, session, navigation or tokenization errors. |
|
|
197
|
+
| `onReady` | `(requestId?: string) => void` | no | Hosted page validated the session and is interactive. |
|
|
198
|
+
| `allowedOrigins` | `readonly string[]` | no | HTTPS origins allowed for navigation and messages. Defaults to the origin of `hostedCardUrl`. |
|
|
199
|
+
| `style` | `StyleProp<ViewStyle>` | no | Style for the underlying WebView. |
|
|
200
|
+
| `testID` | `string` | no | Test identifier forwarded to the WebView. |
|
|
201
|
+
|
|
202
|
+
### `AutraCardTokenizedResult`
|
|
203
|
+
|
|
204
|
+
All fields are **opaque references** — the SDK never exposes card data.
|
|
205
|
+
|
|
206
|
+
| Field | Type | Description |
|
|
207
|
+
| --------------------- | --------- | ------------------------------------------------------------ |
|
|
208
|
+
| `slugStoredCard` | `string?` | Reference your backend uses to charge the stored card later. |
|
|
209
|
+
| `slugToken` | `string?` | Single-purpose token reference. |
|
|
210
|
+
| `tokenExpirationDate` | `string?` | Expiration of the token reference. |
|
|
211
|
+
| `brand` | `string?` | Card brand (display only). |
|
|
212
|
+
| `last4` | `string?` | Last four digits (display only). |
|
|
213
|
+
| `expirationMonth` | `string?` | Card expiration month (display only). |
|
|
214
|
+
| `expirationYear` | `string?` | Card expiration year (display only). |
|
|
215
|
+
| `requestId` | `string?` | Correlation id for support requests. |
|
|
216
|
+
|
|
217
|
+
At least one of `slugStoredCard` / `slugToken` is always present on success.
|
|
218
|
+
|
|
219
|
+
### `AutraCardError`
|
|
220
|
+
|
|
221
|
+
`{ code, message, requestId?, retryable? }`. Handle by `code`; when
|
|
222
|
+
`retryable` is `true`, create a **new** session and retry.
|
|
223
|
+
|
|
224
|
+
| Code | Meaning | What to do |
|
|
225
|
+
| ------------------------- | ------------------------------------------------------------- | ----------------------------------------------- |
|
|
226
|
+
| `configuration_error` | Invalid/non-HTTPS `hostedCardUrl`, or empty `allowedOrigins`. | Fix the integration — not user-recoverable. |
|
|
227
|
+
| `token_expired` | Session expired or unauthorized (sessions are short-lived). | Create a new session and reload the form. |
|
|
228
|
+
| `message_origin_rejected` | Message or navigation from an origin outside the allow-list. | Investigate; do not weaken `allowedOrigins`. |
|
|
229
|
+
| `message_invalid` | Malformed message from the hosted page. | Retry with a new session; report `requestId`. |
|
|
230
|
+
| `validation_error` | Cardholder input rejected. | The hosted page guides the user; no app action. |
|
|
231
|
+
| `tokenization_failed` | Page failed to load or tokenization failed upstream. | If `retryable`, retry with a new session. |
|
|
232
|
+
|
|
233
|
+
### `AUTRA_REACT_NATIVE_CARD_SDK_VERSION`
|
|
234
|
+
|
|
235
|
+
Version marker string, useful for support tickets and smoke tests.
|
|
236
|
+
|
|
237
|
+
## Security & PCI
|
|
238
|
+
|
|
239
|
+
- Card data (PAN, CVV, cardholder data) is entered **only** on the
|
|
240
|
+
Autra-hosted page and sent directly to Autra — it never transits your app or
|
|
241
|
+
backend, which helps reduce your PCI DSS scope.
|
|
242
|
+
- Your app only handles the opaque, short-lived `hostedCardUrl` (in) and the
|
|
243
|
+
opaque `slugStoredCard` / `slugToken` references (out).
|
|
244
|
+
- Keep service credentials (`clientSecret`, strong access tokens) **server-side
|
|
245
|
+
only** — never embed them in the app.
|
|
246
|
+
- Never log PAN/CVV/cardholder data, and never log the `hostedCardUrl`. Use
|
|
247
|
+
`requestId` for correlation.
|
|
248
|
+
- The WebView is hardened by default: HTTPS-only, navigation allow-list,
|
|
249
|
+
no mixed content, incognito, no disk cache, no file access.
|
|
250
|
+
|
|
251
|
+
Found a vulnerability? See [SECURITY.md](./SECURITY.md) — please do not open a
|
|
252
|
+
public issue.
|
|
253
|
+
|
|
254
|
+
## Testing your integration
|
|
255
|
+
|
|
256
|
+
- Use the **sandbox** environment (`https://api.sandbox.autra.io`) with mock
|
|
257
|
+
values only — never real card data.
|
|
258
|
+
<!-- PLACEHOLDER: sandbox test card numbers, if provided by the team -->
|
|
259
|
+
- For unit tests, inject your session-creation function and mock the WebView
|
|
260
|
+
bridge — see
|
|
261
|
+
[`test/functional-app/`](./test/functional-app/FunctionalAutraCardApp.ts)
|
|
262
|
+
for a complete reference harness driving `ready`, `tokenized` and `error`
|
|
263
|
+
flows with a mocked backend.
|
|
264
|
+
|
|
265
|
+
## Troubleshooting
|
|
266
|
+
|
|
267
|
+
- **Blank WebView** — `react-native-webview` is not installed/linked. Install
|
|
268
|
+
it (see [Installation](#installation)) and run `pod install` on iOS.
|
|
269
|
+
- **`configuration_error` immediately** — the `hostedCardUrl` is malformed or
|
|
270
|
+
not HTTPS, or your custom `allowedOrigins` normalized to an empty list.
|
|
271
|
+
- **`token_expired`** — hosted sessions are short-lived and single-use. Create
|
|
272
|
+
a session right before rendering the form, one per attempt.
|
|
273
|
+
- **Nothing happens after submit** — check that you are not filtering the
|
|
274
|
+
hosted page's origin out via a custom `allowedOrigins`.
|
|
275
|
+
- **Cleartext/HTTP hosted URL** — not supported by design; the SDK rejects
|
|
276
|
+
non-HTTPS URLs.
|
|
277
|
+
|
|
278
|
+
## Documentation
|
|
279
|
+
|
|
280
|
+
- Integration guides: developer portal
|
|
281
|
+
<!-- PLACEHOLDER: developer portal URL (EN + PT-BR) -->
|
|
282
|
+
- [Backend flow overview](./docs/integration/backend-flow.md)
|
|
283
|
+
- [SDK scope and limitations](./docs/integration/limitations.md)
|
|
284
|
+
|
|
285
|
+
## Contributing
|
|
286
|
+
|
|
287
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md). Never include real card data or
|
|
288
|
+
secrets in issues, PRs or examples.
|
|
289
|
+
|
|
290
|
+
## Support
|
|
291
|
+
|
|
292
|
+
- Bugs and feature requests:
|
|
293
|
+
[GitHub issues](https://github.com/autratech/autra-card-sdk-react-native/issues)
|
|
294
|
+
- Integration support: <!-- PLACEHOLDER: support channel/email -->
|
|
295
|
+
|
|
296
|
+
## License
|
|
297
|
+
|
|
298
|
+
[MIT](./LICENSE)
|
package/README.pt-BR.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Este arquivo espelha apenas o essencial (features, instalação, quickstart,
|
|
3
|
+
erros) do README.md. Ao alterar o quickstart ou a API no README.md,
|
|
4
|
+
atualize este arquivo também. A documentação completa vive no README.md.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# @autra-io/react-native-card
|
|
8
|
+
|
|
9
|
+
SDK React Native para tokenização segura de cartões via página hospedada da
|
|
10
|
+
Autra. **Os dados do cartão nunca passam pelo seu app nem pelos seus
|
|
11
|
+
servidores.**
|
|
12
|
+
|
|
13
|
+
> 🌎 [Read this README in English](./README.md) (documentação completa) ·
|
|
14
|
+
> Guias completos no portal do desenvolvedor
|
|
15
|
+
> <!-- PLACEHOLDER: URL do portal do desenvolvedor -->
|
|
16
|
+
|
|
17
|
+
## Por que usar
|
|
18
|
+
|
|
19
|
+
- **Tokenização em página hospedada** — o portador digita PAN/CVV em uma
|
|
20
|
+
página hospedada pela Autra, dentro de uma WebView. Os dados do cartão nunca
|
|
21
|
+
transitam pelo seu app ou backend, o que ajuda a reduzir seu escopo PCI DSS.
|
|
22
|
+
- **WebView endurecida por padrão** — HTTPS-only, allow-list de navegação,
|
|
23
|
+
sem conteúdo misto, incognito, sem cache em disco, sem acesso a arquivos.
|
|
24
|
+
- **Bridge de mensagens estrita** — validação de origem e de schema em toda
|
|
25
|
+
mensagem; `onSuccess` dispara no máximo uma vez.
|
|
26
|
+
- **Tipado de ponta a ponta** e **zero dependências de runtime**.
|
|
27
|
+
|
|
28
|
+
## Instalação
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @autra-io/react-native-card react-native-webview
|
|
32
|
+
# iOS (React Native bare): cd ios && pod install
|
|
33
|
+
# Expo: npx expo install react-native-webview
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Requisitos: React >= 18, React Native >= 0.72, `react-native-webview` >= 13
|
|
37
|
+
(peer dependency obrigatória). Expo: requer development build / `expo
|
|
38
|
+
prebuild` (Expo Go não é suportado).
|
|
39
|
+
|
|
40
|
+
## InÃcio rápido
|
|
41
|
+
|
|
42
|
+
### Passo 1 — Backend: criar uma sessão de cartão hospedada
|
|
43
|
+
|
|
44
|
+
As sessões são criadas **no servidor**, com suas credenciais de serviço Autra.
|
|
45
|
+
Nunca chame esta API do app e nunca embuta credenciais no app.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
// Seu backend (exemplo Node). Sandbox: https://api.sandbox.autra.io
|
|
49
|
+
// Produção: consulte o portal do desenvolvedor. <!-- PLACEHOLDER: URL produção -->
|
|
50
|
+
app.post('/card-sessions', async (_req, res) => {
|
|
51
|
+
const response = await fetch('https://api.sandbox.autra.io/v1/acquiring/card-sessions', {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
headers: {/* credenciais de serviço Autra — somente no servidor */},
|
|
54
|
+
});
|
|
55
|
+
const { hostedCardUrl } = await response.json();
|
|
56
|
+
// hostedCardUrl é de curta duração e uso único — gere uma por tentativa.
|
|
57
|
+
res.json({ hostedCardUrl });
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Passo 2 — App: renderizar o formulário
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { useState } from 'react';
|
|
65
|
+
import { Button, View } from 'react-native';
|
|
66
|
+
import {
|
|
67
|
+
AutraCardForm,
|
|
68
|
+
type AutraCardTokenizedResult,
|
|
69
|
+
type AutraCardError,
|
|
70
|
+
} from '@autra-io/react-native-card';
|
|
71
|
+
|
|
72
|
+
export function AdicionarCartaoScreen() {
|
|
73
|
+
const [hostedCardUrl, setHostedCardUrl] = useState<string | null>(null);
|
|
74
|
+
|
|
75
|
+
async function iniciarEntradaDeCartao() {
|
|
76
|
+
// Peça ao SEU backend uma sessão nova (Passo 1). URLs são de uso único:
|
|
77
|
+
// solicite uma nova a cada tentativa — nunca fixe ou reutilize.
|
|
78
|
+
const response = await fetch('https://seu-backend.example.com/card-sessions', {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
});
|
|
81
|
+
const body = (await response.json()) as { hostedCardUrl: string };
|
|
82
|
+
setHostedCardUrl(body.hostedCardUrl);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!hostedCardUrl) {
|
|
86
|
+
return <Button title="Adicionar cartão" onPress={iniciarEntradaDeCartao} />;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<View style={{ flex: 1 }}>
|
|
91
|
+
<AutraCardForm
|
|
92
|
+
hostedCardUrl={hostedCardUrl}
|
|
93
|
+
onReady={() => {
|
|
94
|
+
// Página hospedada pronta — esconda seu indicador de carregamento.
|
|
95
|
+
}}
|
|
96
|
+
onSuccess={(result: AutraCardTokenizedResult) => {
|
|
97
|
+
// Envie result.slugStoredCard ao seu backend e armazene lá.
|
|
98
|
+
// O backend usa essa referência para cobranças futuras.
|
|
99
|
+
}}
|
|
100
|
+
onError={(error: AutraCardError) => {
|
|
101
|
+
if (error.code === 'token_expired' || error.retryable) {
|
|
102
|
+
// Sessões expiram rápido: crie uma NOVA sessão e tente de novo.
|
|
103
|
+
setHostedCardUrl(null);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// Não recuperável: mostre uma mensagem amigável e reporte error.code.
|
|
107
|
+
}}
|
|
108
|
+
style={{ flex: 1 }}
|
|
109
|
+
/>
|
|
110
|
+
</View>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Códigos de erro (`AutraCardError`)
|
|
116
|
+
|
|
117
|
+
| Código | Significado | O que fazer |
|
|
118
|
+
| ------------------------- | ------------------------------------------------------------- | ---------------------------------------------- |
|
|
119
|
+
| `configuration_error` | `hostedCardUrl` inválida/não-HTTPS ou `allowedOrigins` vazio. | Corrigir a integração — não é erro do usuário. |
|
|
120
|
+
| `token_expired` | Sessão expirada ou não autorizada. | Criar nova sessão e recarregar o formulário. |
|
|
121
|
+
| `message_origin_rejected` | Mensagem/navegação de origem fora da allow-list. | Investigar; não enfraquecer `allowedOrigins`. |
|
|
122
|
+
| `message_invalid` | Mensagem malformada da página hospedada. | Tentar com nova sessão; reportar `requestId`. |
|
|
123
|
+
| `validation_error` | Dados do cartão rejeitados. | A própria página orienta o usuário. |
|
|
124
|
+
| `tokenization_failed` | Falha de carregamento ou de tokenização. | Se `retryable`, tentar com nova sessão. |
|
|
125
|
+
|
|
126
|
+
## Documentação completa
|
|
127
|
+
|
|
128
|
+
Referência completa da API, segurança & PCI, testes e troubleshooting:
|
|
129
|
+
[README.md (English)](./README.md).
|
|
130
|
+
|
|
131
|
+
## Licença
|
|
132
|
+
|
|
133
|
+
[MIT](./LICENSE)
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
`@autra-io/react-native-card` is a **payment-adjacent SDK**. Card data and
|
|
4
|
+
credentials demand strict handling. This policy is mandatory for all
|
|
5
|
+
contributors (human and AI).
|
|
6
|
+
|
|
7
|
+
## Reporting a vulnerability
|
|
8
|
+
|
|
9
|
+
- **Do not** open a public GitHub issue for security vulnerabilities.
|
|
10
|
+
- Report privately via
|
|
11
|
+
[GitHub private vulnerability reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability)
|
|
12
|
+
on this repository, or by email to `security@autra.io`
|
|
13
|
+
<!-- PLACEHOLDER: confirm the public security contact email with the team -->.
|
|
14
|
+
- Include: affected version/commit, impact, and reproduction steps using
|
|
15
|
+
**non-sensitive, mock data only**.
|
|
16
|
+
|
|
17
|
+
> **Never** paste PAN, CVV, cardholder data, real tokens, secrets or sensitive
|
|
18
|
+
> logs into an issue, PR, comment or vulnerability report.
|
|
19
|
+
|
|
20
|
+
## Handling card & payment data
|
|
21
|
+
|
|
22
|
+
- **Never** log or store PAN, CVV, expiry or cardholder data — anywhere
|
|
23
|
+
(app logs, SDK payloads, tests, fixtures, docs, examples).
|
|
24
|
+
- **Never** use real card data in tests or examples. Use mock/fake values.
|
|
25
|
+
- Logs and errors reference `requestId` / `correlationId` only.
|
|
26
|
+
- The tokenized card is referenced by an opaque `slugStoredCard`.
|
|
27
|
+
|
|
28
|
+
## Credentials & tokens
|
|
29
|
+
|
|
30
|
+
- **`clientSecret` (OAuth) never goes to the mobile app.**
|
|
31
|
+
- **A strong `accessToken` never goes to the mobile app.**
|
|
32
|
+
- **No provider token of any kind reaches the app.** The app only ever handles
|
|
33
|
+
a short-lived, single-use **`hostedCardUrl`** (hosted card session) created
|
|
34
|
+
by your backend; card entry and tokenization happen inside the hosted page.
|
|
35
|
+
- Never commit secrets, tokens, private keys or credentials.
|
|
36
|
+
- `.env` and `.env.*` are git-ignored; only `.env.example` (placeholders) is
|
|
37
|
+
committed. Real credentials belong in your secret manager — never in the repo.
|
|
38
|
+
|
|
39
|
+
## If a secret is exposed
|
|
40
|
+
|
|
41
|
+
1. **Rotate the secret immediately** (before removing it from history).
|
|
42
|
+
2. Report it through the vulnerability-reporting channel above.
|
|
43
|
+
3. Remove it from the working tree and, if needed, purge from git history.
|
|
44
|
+
4. Verify `gitleaks` passes and confirm no other copies remain.
|
|
45
|
+
|
|
46
|
+
## Security tooling (must stay active)
|
|
47
|
+
|
|
48
|
+
- **pre-commit** hooks (`.pre-commit-config.yaml`): hygiene, secret/env guard,
|
|
49
|
+
gitleaks, prettier, eslint, typecheck, `no-commit-to-branch=main`.
|
|
50
|
+
- **CI security workflow** (`.github/workflows/security.yml`):
|
|
51
|
+
- `npm audit --omit=dev --audit-level=high` (GATE)
|
|
52
|
+
- `gitleaks` secret scan (GATE)
|
|
53
|
+
- `semgrep` SAST (advisory — may become a required gate later)
|
|
54
|
+
|
|
55
|
+
Do not disable or weaken these controls.
|
|
56
|
+
|
|
57
|
+
## Supported versions
|
|
58
|
+
|
|
59
|
+
| Version | Supported |
|
|
60
|
+
| ------- | -------------------------------------- |
|
|
61
|
+
| `0.x` | Latest minor release (pre-1.0 preview) |
|