@inflow_pay/sdk 0.8.0 → 1.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 +11 -0
- package/README.md +283 -594
- package/dist/payment-sdk-CC-mm-nt.js +169 -0
- package/dist/payment-sdk-CC-mm-nt.js.map +1 -0
- package/dist/payment-sdk-oRqgQSx5.mjs +821 -0
- package/dist/payment-sdk-oRqgQSx5.mjs.map +1 -0
- package/dist/react/index.d.ts +27 -36
- package/dist/react.cjs +1 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.esm.js +8 -91
- package/dist/react.esm.js.map +1 -1
- package/dist/react.umd.js +170 -179
- package/dist/react.umd.js.map +1 -1
- package/dist/sdk.cjs +1 -1
- package/dist/sdk.cjs.map +1 -1
- package/dist/sdk.d.ts +33 -158
- package/dist/sdk.esm.js +26 -30
- package/dist/sdk.esm.js.map +1 -1
- package/dist/sdk.umd.js +216 -176
- package/dist/sdk.umd.js.map +1 -1
- package/package.json +6 -6
- package/dist/payment-sdk-CtCxS64i.js +0 -178
- package/dist/payment-sdk-CtCxS64i.js.map +0 -1
- package/dist/payment-sdk-WqMhTy7u.mjs +0 -560
- package/dist/payment-sdk-WqMhTy7u.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
# InflowPay
|
|
1
|
+
# InflowPay SDK v2
|
|
2
2
|
|
|
3
|
-
Accept card payments with a
|
|
3
|
+
Accept card payments with a secure, iframe-based payment form. This version delivers **enhanced security** and **automatic 3D Secure handling** - all with just a few lines of code.
|
|
4
|
+
|
|
5
|
+
## What's Best in This Version
|
|
6
|
+
|
|
7
|
+
- 🔒 **Iframe Architecture**: Card data never touches your server (PCI compliant)
|
|
8
|
+
- ✨ **Built-in Success UI**: Beautiful success screen automatically shown after payment - fully customizable or replaceable
|
|
9
|
+
- 🎯 **Auto 3D Secure**: SDK handles authentication modals automatically
|
|
10
|
+
- 🚀 **Zero Configuration**: Works out of the box with sensible defaults
|
|
11
|
+
- 🎨 **Fully Customizable**: Match your brand with comprehensive styling options
|
|
4
12
|
|
|
5
13
|
## Installation
|
|
6
14
|
|
|
@@ -8,428 +16,312 @@ Accept card payments with a pre-built, secure payment form. Uses an iframe-based
|
|
|
8
16
|
npm install @inflow_pay/sdk
|
|
9
17
|
```
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
**CDN Option** (vanilla HTML pages only):
|
|
12
20
|
|
|
13
21
|
```html
|
|
14
|
-
|
|
15
|
-
<script src="https://cdn.jsdelivr.net/npm/@inflow_pay/sdk@0.8.0/dist/sdk.umd.js"></script>
|
|
16
|
-
<script>
|
|
17
|
-
// SDK is available as window.InflowPaySDK
|
|
18
|
-
const provider = new InflowPaySDK.InflowPayProvider({
|
|
19
|
-
config: { apiKey: 'inflow_pub_xxx' }
|
|
20
|
-
});
|
|
21
|
-
</script>
|
|
22
|
+
<script src="https://cdn.jsdelivr.net/npm/@inflow_pay/sdk@1.1.0/dist/sdk.umd.js"></script>
|
|
22
23
|
```
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
- The example uses exact version `@0.8.0` for production safety (prevents unexpected changes)
|
|
26
|
-
- You can update to newer versions manually when ready (e.g., `@0.8.0`)
|
|
27
|
-
- If you're building a React app (create-react-app, Next.js, etc.), use `npm install` instead of CDN. See [Migration from React SDK](#migration-from-react-sdk) section.
|
|
25
|
+
## Complete Example
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Get started with the iframe-based SDK:
|
|
27
|
+
### Backend: Create Payment
|
|
32
28
|
|
|
33
29
|
```javascript
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
// backend/api/create-payment.js
|
|
31
|
+
app.post("/api/create-payment", async (req, res) => {
|
|
32
|
+
const response = await fetch("https://api.inflowpay.xyz/api/server/payment", {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: {
|
|
35
|
+
"Content-Type": "application/json",
|
|
36
|
+
"X-Inflow-Api-Key": process.env.INFLOW_PRIVATE_KEY,
|
|
37
|
+
},
|
|
38
|
+
body: JSON.stringify({
|
|
39
|
+
products: [{ name: "Product", price: 4999, quantity: 1 }],
|
|
40
|
+
currency: "EUR",
|
|
41
|
+
customerEmail: "customer@example.com",
|
|
42
|
+
firstName: "John",
|
|
43
|
+
lastName: "Doe",
|
|
44
|
+
billingCountry: "FR",
|
|
45
|
+
postalCode: "75001",
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
paymentId: 'pay_xxx', // From your backend
|
|
44
|
-
container: '#card-container',
|
|
45
|
-
onComplete: (result) => {
|
|
46
|
-
if (result.status === 'CHECKOUT_SUCCESS') {
|
|
47
|
-
window.location.href = '/success';
|
|
48
|
-
}
|
|
49
|
-
}
|
|
49
|
+
const data = await response.json();
|
|
50
|
+
res.json({ paymentId: data.payment.id });
|
|
50
51
|
});
|
|
51
|
-
|
|
52
|
-
// Step 3: Mount the CardElement
|
|
53
|
-
cardElement.mount();
|
|
54
52
|
```
|
|
55
53
|
|
|
56
|
-
|
|
54
|
+
### Frontend: React
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
## Payment Status
|
|
56
|
+
```tsx
|
|
57
|
+
import {
|
|
58
|
+
InflowPayProvider,
|
|
59
|
+
CardElement,
|
|
60
|
+
PaymentResultStatus,
|
|
61
|
+
} from "@inflow_pay/sdk/react";
|
|
66
62
|
|
|
67
|
-
|
|
63
|
+
export default function CheckoutPage() {
|
|
64
|
+
const [paymentId, setPaymentId] = useState<string | null>(null);
|
|
68
65
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
| `PAYMENT_FAILED` | Failed | ❌ No |
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
fetch("/api/create-payment", { method: "POST" })
|
|
68
|
+
.then((res) => res.json())
|
|
69
|
+
.then((data) => setPaymentId(data.paymentId));
|
|
70
|
+
}, []);
|
|
75
71
|
|
|
76
|
-
|
|
72
|
+
if (!paymentId) return <div>Loading...</div>;
|
|
77
73
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
74
|
+
return (
|
|
75
|
+
<InflowPayProvider config={{ publicKey: "inflow_pub_xxx" }}>
|
|
76
|
+
<CardElement
|
|
77
|
+
paymentId={paymentId}
|
|
78
|
+
onComplete={(result) => {
|
|
79
|
+
if (result.status === PaymentResultStatus.SUCCESS) {
|
|
80
|
+
setTimeout(() => (window.location.href = "/confirmation"), 2000);
|
|
81
|
+
}
|
|
82
|
+
}}
|
|
83
|
+
style={{
|
|
84
|
+
fontFamily: "Inter",
|
|
85
|
+
button: { backgroundColor: "#0070F3", textColor: "#FFFFFF" },
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
</InflowPayProvider>
|
|
89
|
+
);
|
|
83
90
|
}
|
|
84
91
|
```
|
|
85
92
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
### InflowPayProvider
|
|
93
|
+
### Frontend: Vanilla JavaScript
|
|
89
94
|
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
config: {
|
|
93
|
-
apiKey: 'inflow_pub_xxx', // Required
|
|
94
|
-
iframeUrl: 'https://...', // Optional, auto-detected from API key
|
|
95
|
-
locale: 'en', // Optional, UI language (default: auto-detected from browser). Supported: en, de, es, fr, it, nl, pl, pt
|
|
96
|
-
timeout: 30000, // Optional, default: 30000
|
|
97
|
-
debug: false // Optional, default: false
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
**Locale:** Use `locale` in config to set UI language (e.g. `'fr'`); omit to use the browser language. Supported: en, de, es, fr, it, nl, pl, pt.
|
|
103
|
-
|
|
104
|
-
### CardElement
|
|
105
|
-
|
|
106
|
-
```javascript
|
|
107
|
-
const cardElement = provider.createCardElement({
|
|
108
|
-
paymentId: 'pay_xxx', // Required
|
|
109
|
-
container: '#card-container', // Required (CSS selector or HTMLElement)
|
|
110
|
-
|
|
111
|
-
// Styling (optional)
|
|
112
|
-
style: {
|
|
113
|
-
fontFamily: 'Inter',
|
|
114
|
-
button: {
|
|
115
|
-
backgroundColor: '#0070F3',
|
|
116
|
-
textColor: '#FFFFFF'
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
// Custom button text (optional)
|
|
121
|
-
buttonText: 'Complete Payment',
|
|
122
|
-
|
|
123
|
-
// Custom placeholders (optional)
|
|
124
|
-
placeholders: {
|
|
125
|
-
cardNumber: '1234 5678 9012 3456',
|
|
126
|
-
expiry: 'MM/YY',
|
|
127
|
-
cvc: 'CVC'
|
|
128
|
-
},
|
|
129
|
-
|
|
130
|
-
// Callbacks (same as React SDK)
|
|
131
|
-
onReady: () => {
|
|
132
|
-
// Card element is mounted and ready
|
|
133
|
-
},
|
|
134
|
-
|
|
135
|
-
onChange: (state) => {
|
|
136
|
-
// Track validation state
|
|
137
|
-
// state.complete - whether form is valid
|
|
138
|
-
},
|
|
139
|
-
|
|
140
|
-
onComplete: (result) => {
|
|
141
|
-
// Payment complete (success or failure)
|
|
142
|
-
if (result.error) {
|
|
143
|
-
console.error(result.error.message);
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
|
|
147
|
-
onError: (error) => {
|
|
148
|
-
// SDK-level errors (network, validation)
|
|
149
|
-
console.error(error);
|
|
150
|
-
},
|
|
151
|
-
|
|
152
|
-
onClose: () => {
|
|
153
|
-
// User closed the payment
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// Mount the CardElement
|
|
158
|
-
cardElement.mount();
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### CardElement Methods
|
|
162
|
-
|
|
163
|
-
#### `mount(): void`
|
|
95
|
+
```html
|
|
96
|
+
<div id="card-container"></div>
|
|
164
97
|
|
|
165
|
-
|
|
98
|
+
<script src="https://cdn.jsdelivr.net/npm/@inflow_pay/sdk@1.1.0/dist/sdk.umd.js"></script>
|
|
99
|
+
<script>
|
|
100
|
+
const provider = new InflowPaySDK.InflowPayProvider({
|
|
101
|
+
config: { publicKey: "inflow_pub_xxx" },
|
|
102
|
+
});
|
|
166
103
|
|
|
167
|
-
|
|
168
|
-
|
|
104
|
+
fetch("/api/create-payment", { method: "POST" })
|
|
105
|
+
.then((res) => res.json())
|
|
106
|
+
.then((data) => {
|
|
107
|
+
const cardElement = provider.createCardElement({
|
|
108
|
+
paymentId: data.paymentId,
|
|
109
|
+
container: "#card-container",
|
|
110
|
+
onComplete: (result) => {
|
|
111
|
+
if (result.status === InflowPaySDK.PaymentResultStatus.SUCCESS) {
|
|
112
|
+
setTimeout(() => (window.location.href = "/confirmation"), 2000);
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
cardElement.mount();
|
|
117
|
+
});
|
|
118
|
+
</script>
|
|
169
119
|
```
|
|
170
120
|
|
|
171
|
-
|
|
121
|
+
## Built-in Success UI Explained
|
|
172
122
|
|
|
173
|
-
|
|
123
|
+
The SDK includes a polished success screen that appears automatically after successful payment:
|
|
174
124
|
|
|
175
|
-
|
|
176
|
-
cardElement.destroy();
|
|
177
|
-
```
|
|
125
|
+
**What it shows:**
|
|
178
126
|
|
|
179
|
-
|
|
127
|
+
- ✅ Success icon and "Payment successful!" message
|
|
128
|
+
- 💳 Payment amount (when available from your payment data)
|
|
180
129
|
|
|
181
|
-
|
|
182
|
-
<!DOCTYPE html>
|
|
183
|
-
<html>
|
|
184
|
-
<head>
|
|
185
|
-
<title>My Payment Page</title>
|
|
186
|
-
</head>
|
|
187
|
-
<body>
|
|
188
|
-
<div id="card-container"></div>
|
|
189
|
-
|
|
190
|
-
<script src="https://cdn.jsdelivr.net/npm/@inflow_pay/sdk@0.8.0/dist/sdk.umd.js"></script>
|
|
191
|
-
<script>
|
|
192
|
-
// SDK is available as window.InflowPaySDK
|
|
193
|
-
const provider = new InflowPaySDK.InflowPayProvider({
|
|
194
|
-
config: { apiKey: 'inflow_pub_xxx' }
|
|
195
|
-
});
|
|
130
|
+
**Default vs custom:**
|
|
196
131
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
container: '#card-container',
|
|
200
|
-
onComplete: (result) => {
|
|
201
|
-
if (result.status === InflowPaySDK.PaymentStatus.CHECKOUT_SUCCESS) {
|
|
202
|
-
alert('Payment successful!');
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
});
|
|
132
|
+
- **Default UI** (default): Built-in success screen shows automatically. You don't need to do anything — quick integration, looks great out of the box.
|
|
133
|
+
- **Custom UI:** Set `showDefaultSuccessUI: false` and add your own success UI. Manage the logic in `onComplete`. Full control for custom branding or additional information. Examples below:
|
|
206
134
|
|
|
207
|
-
|
|
208
|
-
</script>
|
|
209
|
-
</body>
|
|
210
|
-
</html>
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
## Error Handling
|
|
214
|
-
|
|
215
|
-
Errors are automatically mapped to user-friendly messages:
|
|
135
|
+
_Vanilla JS:_
|
|
216
136
|
|
|
217
137
|
```javascript
|
|
218
|
-
cardElement
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
138
|
+
const cardElement = provider.createCardElement({
|
|
139
|
+
paymentId: "pay_xxx",
|
|
140
|
+
container: "#card-container",
|
|
141
|
+
showDefaultSuccessUI: false,
|
|
142
|
+
onComplete: (result) => {
|
|
143
|
+
if (result.status === PaymentResultStatus.SUCCESS) {
|
|
144
|
+
document.getElementById("card-container").innerHTML = `
|
|
145
|
+
<div class="custom-success">
|
|
146
|
+
<h2>Thank you for your purchase!</h2>
|
|
147
|
+
<p>Order ID: ${result.paymentId}</p>
|
|
148
|
+
</div>
|
|
149
|
+
`;
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
});
|
|
224
153
|
```
|
|
225
154
|
|
|
226
|
-
|
|
155
|
+
_React:_
|
|
227
156
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
157
|
+
```tsx
|
|
158
|
+
const [result, setResult] = useState<PaymentResult | null>(null);
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<>
|
|
162
|
+
{result ? (
|
|
163
|
+
<div className="custom-success">
|
|
164
|
+
<h2>Thank you for your purchase!</h2>
|
|
165
|
+
<p>Your payment was successful.</p>
|
|
166
|
+
</div>
|
|
167
|
+
) : (
|
|
168
|
+
<InflowPayProvider config={{ publicKey: "inflow_pub_xxx" }}>
|
|
169
|
+
<CardElement
|
|
170
|
+
paymentId="pay_xxx"
|
|
171
|
+
showDefaultSuccessUI={false}
|
|
172
|
+
onComplete={(res) => {
|
|
173
|
+
if (res.status === PaymentResultStatus.SUCCESS) {
|
|
174
|
+
setResult(res);
|
|
175
|
+
}
|
|
176
|
+
}}
|
|
177
|
+
/>
|
|
178
|
+
</InflowPayProvider>
|
|
179
|
+
)}
|
|
180
|
+
</>
|
|
181
|
+
);
|
|
182
|
+
```
|
|
236
183
|
|
|
237
|
-
|
|
238
|
-
2. User completes authentication
|
|
239
|
-
3. Modal closes automatically
|
|
240
|
-
4. Your `onComplete` callback receives the final result
|
|
184
|
+
## Payment Flow
|
|
241
185
|
|
|
242
|
-
|
|
186
|
+
1. Backend creates payment → returns `paymentId`
|
|
187
|
+
2. Frontend initializes SDK with `paymentId` and public key
|
|
188
|
+
3. User enters card details in secure iframe
|
|
189
|
+
4. SDK tokenizes card and processes payment
|
|
190
|
+
5. 3D Secure authentication (if required) - automatic modal
|
|
191
|
+
6. `onComplete` callback fires → redirect or update UI
|
|
243
192
|
|
|
244
|
-
##
|
|
193
|
+
## API Reference
|
|
245
194
|
|
|
246
|
-
|
|
195
|
+
### InflowPayProvider
|
|
247
196
|
|
|
248
197
|
```javascript
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
'Accept': 'application/json',
|
|
254
|
-
'Content-Type': 'application/json',
|
|
255
|
-
'X-Inflow-Api-Key': 'inflow_priv_xxx' // Private key
|
|
198
|
+
const provider = new InflowPayProvider({
|
|
199
|
+
config: {
|
|
200
|
+
publicKey: "inflow_pub_xxx", // Required
|
|
201
|
+
locale: "en", // Optional - defaults to browser language
|
|
256
202
|
},
|
|
257
|
-
body: JSON.stringify({
|
|
258
|
-
products: [{ name: 'Product', price: 4999, quantity: 1 }],
|
|
259
|
-
currency: 'EUR',
|
|
260
|
-
customerEmail: 'customer@example.com',
|
|
261
|
-
billingCountry: 'FR',
|
|
262
|
-
postalCode: '75001',
|
|
263
|
-
firstName: 'John',
|
|
264
|
-
lastName: 'Doe',
|
|
265
|
-
purchasingAsBusiness: false,
|
|
266
|
-
successUrl: 'https://yourdomain.com/success',
|
|
267
|
-
cancelUrl: 'https://yourdomain.com/cancel'
|
|
268
|
-
})
|
|
269
203
|
});
|
|
270
|
-
|
|
271
|
-
const { payment } = await response.json();
|
|
272
|
-
// Send payment.id to your frontend
|
|
273
204
|
```
|
|
274
205
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
## Migration from React SDK
|
|
278
|
-
|
|
279
|
-
The iframe-based SDK maintains similar component APIs, but **styling has changed**. You have two options:
|
|
206
|
+
**Supported locales:** `en`, `de`, `es`, `fr`, `it`, `nl`, `pl`, `pt`
|
|
280
207
|
|
|
281
|
-
###
|
|
282
|
-
|
|
283
|
-
If you're using React, you can use the React components with a similar API:
|
|
284
|
-
|
|
285
|
-
```tsx
|
|
286
|
-
// Change the import path
|
|
287
|
-
import { InflowPayProvider, CardElement } from '@inflow_pay/sdk/react';
|
|
208
|
+
### CardElement
|
|
288
209
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
paymentId={paymentId}
|
|
294
|
-
style={{
|
|
295
|
-
// ⚠️ Styling API has changed - see Styling section below
|
|
296
|
-
fontFamily: 'Inter',
|
|
297
|
-
button: {
|
|
298
|
-
backgroundColor: '#0070F3',
|
|
299
|
-
textColor: '#FFFFFF'
|
|
300
|
-
}
|
|
301
|
-
}}
|
|
302
|
-
onComplete={(result) => {
|
|
303
|
-
if (result.status === 'CHECKOUT_SUCCESS') {
|
|
304
|
-
window.location.href = '/success';
|
|
305
|
-
}
|
|
306
|
-
}}
|
|
307
|
-
/>
|
|
308
|
-
</InflowPayProvider>
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
```
|
|
210
|
+
```javascript
|
|
211
|
+
const cardElement = provider.createCardElement({
|
|
212
|
+
paymentId: "pay_xxx", // Required - from your backend
|
|
213
|
+
container: "#card-container", // Required - CSS selector or HTMLElement
|
|
312
214
|
|
|
313
|
-
|
|
215
|
+
// Callbacks
|
|
216
|
+
onComplete: (result) => {
|
|
217
|
+
if (result.status === PaymentResultStatus.SUCCESS) {
|
|
218
|
+
// Payment successful
|
|
219
|
+
} else if (result.status === PaymentResultStatus.FAILED) {
|
|
220
|
+
// Payment failed - result.error contains details
|
|
221
|
+
}
|
|
222
|
+
},
|
|
314
223
|
|
|
315
|
-
|
|
224
|
+
onReady: () => {
|
|
225
|
+
// Card element mounted and ready
|
|
226
|
+
},
|
|
316
227
|
|
|
317
|
-
|
|
228
|
+
onChange: (state) => {
|
|
229
|
+
// state.complete - whether form is valid
|
|
230
|
+
},
|
|
318
231
|
|
|
319
|
-
|
|
320
|
-
|
|
232
|
+
onError: (error) => {
|
|
233
|
+
// SDK-level errors
|
|
234
|
+
},
|
|
321
235
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
236
|
+
// Styling (optional)
|
|
237
|
+
style: {
|
|
238
|
+
fontFamily: "Inter",
|
|
239
|
+
button: {
|
|
240
|
+
backgroundColor: "#0070F3",
|
|
241
|
+
textColor: "#FFFFFF",
|
|
242
|
+
},
|
|
243
|
+
},
|
|
325
244
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
}
|
|
245
|
+
// Custom text (optional)
|
|
246
|
+
buttonText: "Pay Now",
|
|
247
|
+
placeholders: {
|
|
248
|
+
cardNumber: "1234 5678 9012 3456",
|
|
249
|
+
expiry: "MM/YY",
|
|
250
|
+
cvc: "CVC",
|
|
251
|
+
},
|
|
334
252
|
});
|
|
335
253
|
|
|
336
254
|
cardElement.mount();
|
|
337
255
|
```
|
|
338
256
|
|
|
339
|
-
|
|
340
|
-
- No React required - works with vanilla JavaScript
|
|
341
|
-
- Explicit `container` prop (CSS selector or HTMLElement) instead of JSX
|
|
342
|
-
- Call `mount()` explicitly instead of automatic React mounting
|
|
343
|
-
- Same callbacks, same status codes, same error handling
|
|
344
|
-
- **Styling API has changed** - see [Styling](#styling) section for details
|
|
345
|
-
|
|
346
|
-
## TypeScript
|
|
347
|
-
|
|
348
|
-
Full type definitions included:
|
|
257
|
+
### Payment Result
|
|
349
258
|
|
|
350
259
|
```typescript
|
|
351
|
-
|
|
352
|
-
|
|
260
|
+
interface PaymentResult {
|
|
261
|
+
status: "SUCCESS" | "FAILED";
|
|
262
|
+
paymentId: string;
|
|
263
|
+
error?: {
|
|
264
|
+
code: string; // Error code (e.g., 'THREE_DS_FAILED')
|
|
265
|
+
message: string; // User-friendly message
|
|
266
|
+
retryable: boolean; // Can retry?
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
```
|
|
353
270
|
|
|
354
|
-
|
|
355
|
-
config: { apiKey: 'inflow_pub_xxx' }
|
|
356
|
-
});
|
|
271
|
+
### CardElement Methods
|
|
357
272
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
onComplete: (result: PaymentResult) => {
|
|
362
|
-
if (result.status === PaymentStatus.CHECKOUT_SUCCESS) {
|
|
363
|
-
// Success
|
|
364
|
-
}
|
|
365
|
-
},
|
|
366
|
-
onError: (error: PaymentError) => {
|
|
367
|
-
console.error(error);
|
|
368
|
-
}
|
|
369
|
-
});
|
|
273
|
+
```javascript
|
|
274
|
+
cardElement.mount(); // Mount to DOM
|
|
275
|
+
cardElement.destroy(); // Cleanup and unmount
|
|
370
276
|
```
|
|
371
277
|
|
|
372
278
|
## Styling
|
|
373
279
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
### Basic Styling
|
|
280
|
+
Customize the payment form to match your brand:
|
|
377
281
|
|
|
378
282
|
```javascript
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
style: {
|
|
383
|
-
fontFamily: 'Inter',
|
|
384
|
-
fillParent: true,
|
|
385
|
-
inputContainer: {
|
|
386
|
-
backgroundColor: '#F5F5F5',
|
|
387
|
-
borderRadius: '8px',
|
|
388
|
-
borderEnabled: true,
|
|
389
|
-
borderColor: '#E0E0E0'
|
|
390
|
-
},
|
|
391
|
-
input: {
|
|
392
|
-
textColor: '#1A1A1A',
|
|
393
|
-
placeholderColor: '#999999'
|
|
394
|
-
},
|
|
395
|
-
button: {
|
|
396
|
-
backgroundColor: '#0070F3',
|
|
397
|
-
textColor: '#FFFFFF',
|
|
398
|
-
borderRadius: '8px',
|
|
399
|
-
fontSize: '16px',
|
|
400
|
-
fontWeight: 600,
|
|
401
|
-
opacity: 1,
|
|
402
|
-
hover: {
|
|
403
|
-
backgroundColor: '#0051CC'
|
|
404
|
-
},
|
|
405
|
-
disabled: {
|
|
406
|
-
opacity: 0.5
|
|
407
|
-
}
|
|
408
|
-
},
|
|
409
|
-
dark: {
|
|
410
|
-
button: {
|
|
411
|
-
backgroundColor: '#0066CC'
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
});
|
|
416
|
-
```
|
|
283
|
+
style: {
|
|
284
|
+
fontFamily: 'Inter', // 'DM Sans' | 'Inter' | 'Poppins' | 'Nunito' | etc.
|
|
285
|
+
fillParent: true,
|
|
417
286
|
|
|
418
|
-
|
|
287
|
+
// Input container
|
|
288
|
+
inputContainer: {
|
|
289
|
+
backgroundColor: '#F5F5F5',
|
|
290
|
+
borderRadius: '8px',
|
|
291
|
+
borderEnabled: true,
|
|
292
|
+
borderColor: '#E0E0E0'
|
|
293
|
+
},
|
|
419
294
|
|
|
420
|
-
|
|
295
|
+
// Input fields
|
|
296
|
+
input: {
|
|
297
|
+
textColor: '#1A1A1A',
|
|
298
|
+
placeholderColor: '#999999',
|
|
299
|
+
backgroundColor: 'transparent'
|
|
300
|
+
},
|
|
421
301
|
|
|
422
|
-
|
|
423
|
-
style: {
|
|
424
|
-
fontFamily: 'Inter',
|
|
302
|
+
// Button
|
|
425
303
|
button: {
|
|
426
304
|
backgroundColor: '#0070F3',
|
|
427
|
-
textColor: '#FFFFFF'
|
|
305
|
+
textColor: '#FFFFFF',
|
|
306
|
+
borderRadius: '8px',
|
|
307
|
+
fontSize: '16px',
|
|
308
|
+
fontWeight: 600,
|
|
309
|
+
hover: {
|
|
310
|
+
backgroundColor: '#0051CC'
|
|
311
|
+
},
|
|
312
|
+
disabled: {
|
|
313
|
+
opacity: 0.5
|
|
314
|
+
}
|
|
428
315
|
},
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
316
|
+
|
|
317
|
+
// Success screen (shown by default; customize with successUI)
|
|
318
|
+
successUI: {
|
|
319
|
+
backgroundColor: '#F5F5F5',
|
|
320
|
+
primaryTextColor: '#1A1A1A', // title and amount
|
|
321
|
+
secondaryTextColor: '#999999' // subtitle
|
|
432
322
|
},
|
|
323
|
+
|
|
324
|
+
// Dark mode
|
|
433
325
|
dark: {
|
|
434
326
|
inputContainer: {
|
|
435
327
|
backgroundColor: '#1A1A1A',
|
|
@@ -437,281 +329,78 @@ style: {
|
|
|
437
329
|
},
|
|
438
330
|
input: {
|
|
439
331
|
textColor: '#FFFFFF',
|
|
440
|
-
|
|
332
|
+
placeholderColor: '#959499'
|
|
441
333
|
},
|
|
442
334
|
button: {
|
|
443
335
|
backgroundColor: '#0066CC'
|
|
336
|
+
},
|
|
337
|
+
successUI: {
|
|
338
|
+
backgroundColor: '#1A1A1A',
|
|
339
|
+
primaryTextColor: '#FFFFFF',
|
|
340
|
+
secondaryTextColor: '#959499'
|
|
444
341
|
}
|
|
445
342
|
}
|
|
446
343
|
}
|
|
447
344
|
```
|
|
448
345
|
|
|
449
|
-
|
|
346
|
+
**Available style properties:** `inputContainer`, `input`, `button`, `successUI`, `generalError`, `disclaimerColor`, `fieldErrorColor`, `dark` (for dark mode overrides)
|
|
450
347
|
|
|
451
|
-
|
|
452
|
-
type FontFamily =
|
|
453
|
-
| 'DM Sans'
|
|
454
|
-
| 'Inter'
|
|
455
|
-
| 'Poppins'
|
|
456
|
-
| 'Nunito'
|
|
457
|
-
| 'Work Sans'
|
|
458
|
-
| 'Manrope'
|
|
459
|
-
| 'Rubik'
|
|
460
|
-
| 'Karla'
|
|
461
|
-
| 'Figtree'
|
|
462
|
-
| 'Outfit'
|
|
463
|
-
| 'Space Grotesk'
|
|
464
|
-
| 'Urbanist';
|
|
465
|
-
```
|
|
348
|
+
See full TypeScript types in the package for all styling options.
|
|
466
349
|
|
|
467
|
-
|
|
350
|
+
## Error Handling
|
|
468
351
|
|
|
469
|
-
|
|
352
|
+
```javascript
|
|
353
|
+
import { PaymentResultStatus, PaymentResultErrorCode } from "@inflow_pay/sdk";
|
|
470
354
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
inputContainer?: {
|
|
479
|
-
backgroundColor?: string;
|
|
480
|
-
borderColor?: string;
|
|
481
|
-
borderEnabled?: boolean; // Must be true to show border
|
|
482
|
-
borderRadius?: string;
|
|
483
|
-
};
|
|
484
|
-
|
|
485
|
-
// Individual input fields
|
|
486
|
-
input?: {
|
|
487
|
-
backgroundColor?: string;
|
|
488
|
-
borderColor?: string;
|
|
489
|
-
borderEnabled?: boolean;
|
|
490
|
-
borderRadius?: string;
|
|
491
|
-
textColor?: string;
|
|
492
|
-
placeholderColor?: string;
|
|
493
|
-
};
|
|
494
|
-
|
|
495
|
-
// Button styles
|
|
496
|
-
button?: {
|
|
497
|
-
backgroundColor?: string;
|
|
498
|
-
textColor?: string;
|
|
499
|
-
borderRadius?: string;
|
|
500
|
-
borderColor?: string;
|
|
501
|
-
borderEnabled?: boolean; // Must be true to show border
|
|
502
|
-
fontSize?: string;
|
|
503
|
-
fontWeight?: FontWeight; // 100-900 | 'normal' | 'bold' | 'lighter' | 'bolder'
|
|
504
|
-
opacity?: Opacity; // number (0-1) | string ('0.5' | '50%')
|
|
505
|
-
hover?: { // Hover state
|
|
506
|
-
backgroundColor?: string;
|
|
507
|
-
textColor?: string;
|
|
508
|
-
borderRadius?: string;
|
|
509
|
-
borderColor?: string;
|
|
510
|
-
borderEnabled?: boolean;
|
|
511
|
-
fontSize?: string;
|
|
512
|
-
fontWeight?: FontWeight;
|
|
513
|
-
opacity?: Opacity;
|
|
514
|
-
};
|
|
515
|
-
disabled?: { // Disabled state
|
|
516
|
-
backgroundColor?: string;
|
|
517
|
-
textColor?: string;
|
|
518
|
-
borderRadius?: string;
|
|
519
|
-
borderColor?: string;
|
|
520
|
-
borderEnabled?: boolean;
|
|
521
|
-
fontSize?: string;
|
|
522
|
-
fontWeight?: FontWeight;
|
|
523
|
-
opacity?: Opacity;
|
|
524
|
-
};
|
|
525
|
-
loaderColor?: string; // Loading spinner color
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
// General error message
|
|
529
|
-
generalError?: {
|
|
530
|
-
textColor?: string;
|
|
531
|
-
backgroundColor?: string;
|
|
532
|
-
borderEnabled?: boolean;
|
|
533
|
-
borderRadius?: string;
|
|
534
|
-
borderColor?: string;
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
// General success message
|
|
538
|
-
generalSuccess?: {
|
|
539
|
-
textColor?: string;
|
|
540
|
-
backgroundColor?: string;
|
|
541
|
-
borderEnabled?: boolean;
|
|
542
|
-
borderRadius?: string;
|
|
543
|
-
borderColor?: string;
|
|
544
|
-
};
|
|
545
|
-
|
|
546
|
-
// Other colors
|
|
547
|
-
disclaimerColor?: string; // Disclaimer text and icon color
|
|
548
|
-
fieldErrorColor?: string; // Field validation error text color
|
|
549
|
-
|
|
550
|
-
// Dark mode overrides
|
|
551
|
-
dark?: {
|
|
552
|
-
inputContainer?: InputContainerStyles;
|
|
553
|
-
input?: InputStyles;
|
|
554
|
-
button?: ButtonStyles;
|
|
555
|
-
disclaimerColor?: string;
|
|
556
|
-
fieldErrorColor?: string;
|
|
557
|
-
generalError?: GeneralMessageStyles;
|
|
558
|
-
generalSuccess?: GeneralMessageStyles;
|
|
559
|
-
};
|
|
560
|
-
}
|
|
355
|
+
onComplete: (result) => {
|
|
356
|
+
if (result.status === PaymentResultStatus.FAILED && result.error) {
|
|
357
|
+
console.log(result.error.code); // e.g., 'THREE_DS_FAILED'
|
|
358
|
+
console.log(result.error.message); // User-friendly message
|
|
359
|
+
console.log(result.error.retryable); // Can user retry?
|
|
360
|
+
}
|
|
361
|
+
};
|
|
561
362
|
```
|
|
562
363
|
|
|
563
|
-
|
|
364
|
+
**Common error codes:** `THREE_DS_FAILED`, `PAYMENT_PROCESSING_ERROR`
|
|
564
365
|
|
|
565
|
-
|
|
566
|
-
placeholders: {
|
|
567
|
-
cardNumber: '1234 5678 9012 3456',
|
|
568
|
-
expiry: 'MM/YY',
|
|
569
|
-
cvc: 'CVC'
|
|
570
|
-
}
|
|
571
|
-
```
|
|
366
|
+
## 3D Secure
|
|
572
367
|
|
|
573
|
-
|
|
368
|
+
3DS is handled automatically by the SDK. When required, a modal opens for bank authentication, then closes automatically. No setup needed.
|
|
574
369
|
|
|
575
|
-
|
|
576
|
-
buttonText: 'Pay Now'
|
|
577
|
-
```
|
|
370
|
+
## Migration from React SDK
|
|
578
371
|
|
|
579
|
-
|
|
372
|
+
**For React apps:** Import from `@inflow_pay/sdk/react` instead. The component API is similar, but the styling structure has changed (see [Styling](#styling) section).
|
|
580
373
|
|
|
581
|
-
|
|
582
|
-
import { InflowPayProvider, CardElement } from '@inflow_pay/sdk/react';
|
|
374
|
+
**For non-React apps:** Use the vanilla JavaScript API shown in the examples above.
|
|
583
375
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
<CardElement
|
|
588
|
-
paymentId="pay_xxx"
|
|
589
|
-
style={{
|
|
590
|
-
fontFamily: 'Inter',
|
|
591
|
-
fillParent: true,
|
|
592
|
-
|
|
593
|
-
// Input container
|
|
594
|
-
inputContainer: {
|
|
595
|
-
backgroundColor: '#F5F5F5',
|
|
596
|
-
borderRadius: '8px',
|
|
597
|
-
borderEnabled: true,
|
|
598
|
-
borderColor: '#E0E0E0'
|
|
599
|
-
},
|
|
600
|
-
|
|
601
|
-
// Input fields
|
|
602
|
-
input: {
|
|
603
|
-
textColor: '#1A1A1A',
|
|
604
|
-
placeholderColor: '#999999',
|
|
605
|
-
backgroundColor: 'transparent',
|
|
606
|
-
borderColor: '#CECECE',
|
|
607
|
-
borderEnabled: true,
|
|
608
|
-
borderRadius: '6px'
|
|
609
|
-
},
|
|
610
|
-
|
|
611
|
-
// Button
|
|
612
|
-
button: {
|
|
613
|
-
backgroundColor: '#0070F3',
|
|
614
|
-
textColor: '#FFFFFF',
|
|
615
|
-
borderRadius: '8px',
|
|
616
|
-
fontSize: '16px',
|
|
617
|
-
fontWeight: 600,
|
|
618
|
-
opacity: 1,
|
|
619
|
-
borderEnabled: false,
|
|
620
|
-
hover: {
|
|
621
|
-
backgroundColor: '#0051CC',
|
|
622
|
-
opacity: 1
|
|
623
|
-
},
|
|
624
|
-
disabled: {
|
|
625
|
-
opacity: 0.5
|
|
626
|
-
},
|
|
627
|
-
loaderColor: '#FFFFFF'
|
|
628
|
-
},
|
|
629
|
-
|
|
630
|
-
// General messages
|
|
631
|
-
generalError: {
|
|
632
|
-
textColor: '#FF443F',
|
|
633
|
-
backgroundColor: '#ffd6cc',
|
|
634
|
-
borderColor: '#FF443F',
|
|
635
|
-
borderEnabled: true,
|
|
636
|
-
borderRadius: '6px'
|
|
637
|
-
},
|
|
638
|
-
|
|
639
|
-
generalSuccess: {
|
|
640
|
-
textColor: '#29a329',
|
|
641
|
-
backgroundColor: '#d6f5d6',
|
|
642
|
-
borderColor: '#29a329',
|
|
643
|
-
borderEnabled: true,
|
|
644
|
-
borderRadius: '6px'
|
|
645
|
-
},
|
|
646
|
-
|
|
647
|
-
// Other colors
|
|
648
|
-
disclaimerColor: '#7A7A7A',
|
|
649
|
-
fieldErrorColor: '#ef4444',
|
|
650
|
-
|
|
651
|
-
// Dark mode
|
|
652
|
-
dark: {
|
|
653
|
-
inputContainer: {
|
|
654
|
-
backgroundColor: '#2d2d2d',
|
|
655
|
-
borderColor: '#7A7A7A'
|
|
656
|
-
},
|
|
657
|
-
input: {
|
|
658
|
-
textColor: '#FFFFFF',
|
|
659
|
-
placeholderColor: '#959499',
|
|
660
|
-
borderColor: '#7A7A7A'
|
|
661
|
-
},
|
|
662
|
-
button: {
|
|
663
|
-
backgroundColor: '#0066CC'
|
|
664
|
-
},
|
|
665
|
-
generalError: {
|
|
666
|
-
textColor: '#ffc2b3',
|
|
667
|
-
backgroundColor: '#991f00',
|
|
668
|
-
borderColor: '#e62e00'
|
|
669
|
-
},
|
|
670
|
-
generalSuccess: {
|
|
671
|
-
textColor: '#adebad',
|
|
672
|
-
backgroundColor: '#196619',
|
|
673
|
-
borderColor: '#2eb82e'
|
|
674
|
-
},
|
|
675
|
-
disclaimerColor: '#959499',
|
|
676
|
-
fieldErrorColor: '#ef4444'
|
|
677
|
-
}
|
|
678
|
-
}}
|
|
679
|
-
buttonText="Complete Payment"
|
|
680
|
-
onComplete={(result) => {
|
|
681
|
-
if (result.status === 'CHECKOUT_SUCCESS') {
|
|
682
|
-
alert('Payment successful!');
|
|
683
|
-
}
|
|
684
|
-
}}
|
|
685
|
-
/>
|
|
686
|
-
</InflowPayProvider>
|
|
687
|
-
);
|
|
688
|
-
}
|
|
689
|
-
```
|
|
376
|
+
## TypeScript
|
|
377
|
+
|
|
378
|
+
Full type definitions included:
|
|
690
379
|
|
|
691
|
-
|
|
380
|
+
```typescript
|
|
381
|
+
import { InflowPayProvider, PaymentResultStatus } from "@inflow_pay/sdk";
|
|
382
|
+
import type { PaymentResult, PaymentError } from "@inflow_pay/sdk";
|
|
692
383
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
- ✅ Iframe isolation for security
|
|
384
|
+
const provider = new InflowPayProvider({
|
|
385
|
+
config: { publicKey: "inflow_pub_xxx" },
|
|
386
|
+
});
|
|
387
|
+
```
|
|
698
388
|
|
|
699
389
|
## Features
|
|
700
390
|
|
|
701
|
-
- ✅
|
|
702
|
-
- ✅
|
|
703
|
-
- ✅
|
|
704
|
-
- ✅
|
|
705
|
-
- ✅
|
|
706
|
-
- ✅
|
|
707
|
-
- ✅ **Secure**: PCI compliant, iframe isolation
|
|
391
|
+
- ✅ Dynamic height adjustment
|
|
392
|
+
- ✅ Skeleton loader with shimmer effect
|
|
393
|
+
- ✅ Dark mode support
|
|
394
|
+
- ✅ Responsive design
|
|
395
|
+
- ✅ WCAG compliant
|
|
396
|
+
- ✅ Multi-language support
|
|
708
397
|
|
|
709
398
|
## Support
|
|
710
399
|
|
|
711
|
-
- **
|
|
400
|
+
- **Docs:** https://docs.inflowpay.com/v0/reference/createpayment
|
|
712
401
|
- **Email:** support@inflowpay.com
|
|
713
402
|
- **GitHub:** https://github.com/inflowpay/sdk
|
|
714
|
-
- **Changelog:**
|
|
403
|
+
- **Changelog:** [CHANGELOG.md](./CHANGELOG.md)
|
|
715
404
|
|
|
716
405
|
## License
|
|
717
406
|
|