@bloque/payments-react 0.0.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.
- package/README.md +319 -0
- package/dist/bloque-checkout.d.ts +25 -0
- package/dist/bloque-checkout.js +50 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/package.json +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# @bloque/payments-react
|
|
2
|
+
|
|
3
|
+
React wrapper for `@bloque/payments-elements` web components.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Using bun
|
|
9
|
+
bun add @bloque/payments-react
|
|
10
|
+
|
|
11
|
+
# Using pnpm
|
|
12
|
+
pnpm add @bloque/payments-react
|
|
13
|
+
|
|
14
|
+
# Using npm
|
|
15
|
+
npm install @bloque/payments-react
|
|
16
|
+
|
|
17
|
+
# Using yarn
|
|
18
|
+
yarn add @bloque/payments-react
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### How it Works
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐
|
|
27
|
+
│ React Component │─────▶│ Your Backend │─────▶│ Bloque API │
|
|
28
|
+
│ (Frontend) │ │ (SDK Usage) │ │ │
|
|
29
|
+
└─────────────────┘ └─────────────────┘ └──────────────┘
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
1. **React Component** captures payment data from user
|
|
33
|
+
2. **`onSubmit`** sends complete `PaymentSubmitPayload` to your backend
|
|
34
|
+
3. **Your Backend** uses `@bloque/payments` to process the payment
|
|
35
|
+
|
|
36
|
+
### Basic Example
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { BloqueCheckout } from '@bloque/payments-react';
|
|
40
|
+
import type { PaymentSubmitPayload } from '@bloque/payments-react';
|
|
41
|
+
|
|
42
|
+
function App() {
|
|
43
|
+
const handleSubmit = async (payload: PaymentSubmitPayload) => {
|
|
44
|
+
const response = await fetch('/api/payments', {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: { 'Content-Type': 'application/json' },
|
|
47
|
+
body: JSON.stringify(payload),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new Error('Payment failed');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return response.json();
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<BloqueCheckout
|
|
59
|
+
onSubmit={handleSubmit}
|
|
60
|
+
requireEmail={true}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Your Backend (using `@bloque/payments`):**
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { Bloque } from '@bloque/payments';
|
|
70
|
+
|
|
71
|
+
const bloque = new Bloque({
|
|
72
|
+
apiKey: process.env.BLOQUE_API_KEY!,
|
|
73
|
+
server: 'production',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
app.post('/api/payments', async (req, res) => {
|
|
77
|
+
const payload = req.body;
|
|
78
|
+
|
|
79
|
+
const payment = await bloque.payments.create({
|
|
80
|
+
payment: payload,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
res.json(payment);
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### With Configuration and Appearance
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
import { BloqueCheckout } from '@bloque/payments-react';
|
|
91
|
+
import type {
|
|
92
|
+
CheckoutConfig,
|
|
93
|
+
AppearanceConfig,
|
|
94
|
+
PaymentSubmitPayload,
|
|
95
|
+
} from '@bloque/payments-react';
|
|
96
|
+
|
|
97
|
+
function CheckoutPage() {
|
|
98
|
+
const config: CheckoutConfig = {
|
|
99
|
+
payment_methods: ['card', 'pse'],
|
|
100
|
+
amount: 120_000,
|
|
101
|
+
currency: 'COP',
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const appearance: AppearanceConfig = {
|
|
105
|
+
primaryColor: '#10b981',
|
|
106
|
+
borderRadius: '12px',
|
|
107
|
+
fontFamily: 'Inter, system-ui, sans-serif',
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const handleSubmit = async (payload: PaymentSubmitPayload) => {
|
|
111
|
+
const response = await fetch('/api/payments', {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: { 'Content-Type': 'application/json' },
|
|
114
|
+
body: JSON.stringify(payload),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
throw new Error('Payment failed');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return response.json();
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const handleSuccess = (event: CustomEvent<PaymentSubmitPayload>) => {
|
|
125
|
+
console.log('Payment successful!', event.detail);
|
|
126
|
+
window.location.href = '/success';
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const handleError = (event: CustomEvent<PaymentSubmitPayload & { error: string }>) => {
|
|
130
|
+
console.error('Payment failed:', event.detail.error);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<BloqueCheckout
|
|
135
|
+
config={config}
|
|
136
|
+
appearance={appearance}
|
|
137
|
+
onSubmit={handleSubmit}
|
|
138
|
+
onSuccess={handleSuccess}
|
|
139
|
+
onError={handleError}
|
|
140
|
+
/>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### With Custom Styling
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import { BloqueCheckout } from '@bloque/payments-react';
|
|
149
|
+
|
|
150
|
+
function App() {
|
|
151
|
+
return (
|
|
152
|
+
<div className="checkout-container">
|
|
153
|
+
<BloqueCheckout
|
|
154
|
+
onSubmit={handleSubmit}
|
|
155
|
+
className="custom-checkout"
|
|
156
|
+
style={{ maxWidth: '600px', margin: '0 auto' }}
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Props
|
|
164
|
+
|
|
165
|
+
### `BloqueCheckout`
|
|
166
|
+
|
|
167
|
+
| Prop | Type | Default | Description |
|
|
168
|
+
|------|------|---------|-------------|
|
|
169
|
+
| `config` | `CheckoutConfig` | `undefined` | Configuration object from backend SDK |
|
|
170
|
+
| `appearance` | `AppearanceConfig` | `undefined` | Appearance customization |
|
|
171
|
+
| `amount` | `number` | `undefined` | Payment amount (can also be set via `config.amount`) |
|
|
172
|
+
| `availableMethods` | `PaymentMethodType[]` | `['card', 'pse', 'cash']` | Available payment methods |
|
|
173
|
+
| `requireEmail` | `boolean` | `true` | Whether email is required for card payments |
|
|
174
|
+
| `showMethodSelector` | `boolean` | `true` | Whether to show the payment method selector |
|
|
175
|
+
| `onSubmit` | `function` | `undefined` | Function called when user submits payment |
|
|
176
|
+
| `onSuccess` | `function` | `undefined` | Event handler for successful payment |
|
|
177
|
+
| `onError` | `function` | `undefined` | Event handler for payment errors |
|
|
178
|
+
| `className` | `string` | `undefined` | CSS class name |
|
|
179
|
+
| `style` | `React.CSSProperties` | `undefined` | Inline styles |
|
|
180
|
+
|
|
181
|
+
### `CheckoutConfig`
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
interface CheckoutConfig {
|
|
185
|
+
payment_methods?: PaymentMethodType[];
|
|
186
|
+
amount?: number;
|
|
187
|
+
currency?: string;
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### `AppearanceConfig`
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
interface AppearanceConfig {
|
|
195
|
+
primaryColor?: string;
|
|
196
|
+
borderRadius?: string;
|
|
197
|
+
fontFamily?: string;
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## TypeScript
|
|
202
|
+
|
|
203
|
+
The package includes full TypeScript definitions with discriminated union types for type safety:
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import type {
|
|
207
|
+
BloqueCheckoutProps,
|
|
208
|
+
PaymentSubmitPayload,
|
|
209
|
+
CheckoutConfig,
|
|
210
|
+
AppearanceConfig,
|
|
211
|
+
} from '@bloque/payments-react';
|
|
212
|
+
|
|
213
|
+
// PaymentSubmitPayload is a discriminated union type
|
|
214
|
+
// TypeScript automatically narrows the type based on the 'type' field
|
|
215
|
+
const handleSubmit = async (payload: PaymentSubmitPayload) => {
|
|
216
|
+
// Type narrowing example
|
|
217
|
+
switch (payload.type) {
|
|
218
|
+
case 'card':
|
|
219
|
+
// payload.data is CardPaymentFormData
|
|
220
|
+
console.log('Card ending in:', payload.data.cardNumber.slice(-4));
|
|
221
|
+
break;
|
|
222
|
+
case 'pse':
|
|
223
|
+
// payload.data is PSEPaymentFormData
|
|
224
|
+
console.log('Bank:', payload.data.bankCode);
|
|
225
|
+
break;
|
|
226
|
+
case 'cash':
|
|
227
|
+
// payload.data is CashPaymentFormData
|
|
228
|
+
console.log('Name:', payload.data.fullName);
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Send complete payload to your backend
|
|
233
|
+
const response = await fetch('/api/payments', {
|
|
234
|
+
method: 'POST',
|
|
235
|
+
headers: { 'Content-Type': 'application/json' },
|
|
236
|
+
body: JSON.stringify(payload),
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
return response.json();
|
|
240
|
+
};
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Events
|
|
244
|
+
|
|
245
|
+
### `onSubmit`
|
|
246
|
+
|
|
247
|
+
Called when the user submits a payment form. Send the complete payload to your backend, which will use `@bloque/payments` to process the payment.
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
onSubmit?: (payload: PaymentSubmitPayload) => Promise<void>;
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Example:**
|
|
254
|
+
```typescript
|
|
255
|
+
const handleSubmit = async (payload: PaymentSubmitPayload) => {
|
|
256
|
+
const response = await fetch('/api/payments', {
|
|
257
|
+
method: 'POST',
|
|
258
|
+
headers: { 'Content-Type': 'application/json' },
|
|
259
|
+
body: JSON.stringify(payload),
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
if (!response.ok) throw new Error('Payment failed');
|
|
263
|
+
return response.json();
|
|
264
|
+
};
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Type definition:**
|
|
268
|
+
```typescript
|
|
269
|
+
type PaymentSubmitPayload =
|
|
270
|
+
| { type: 'card'; data: CardPaymentFormData }
|
|
271
|
+
| { type: 'pse'; data: PSEPaymentFormData }
|
|
272
|
+
| { type: 'cash'; data: CashPaymentFormData };
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Your backend should:**
|
|
276
|
+
```typescript
|
|
277
|
+
import { Bloque } from '@bloque/payments';
|
|
278
|
+
|
|
279
|
+
const bloque = new Bloque({
|
|
280
|
+
apiKey: process.env.BLOQUE_API_KEY!,
|
|
281
|
+
server: 'production',
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
app.post('/api/payments', async (req, res) => {
|
|
285
|
+
const payload = req.body;
|
|
286
|
+
|
|
287
|
+
const payment = await bloque.payments.create({
|
|
288
|
+
payment: payload,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
res.json(payment);
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### `onSuccess`
|
|
296
|
+
|
|
297
|
+
Fired when the payment is successfully processed.
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
onSuccess?: (event: CustomEvent<PaymentSubmitPayload>) => void;
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### `onError`
|
|
304
|
+
|
|
305
|
+
Fired when payment processing fails.
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
onError?: (event: CustomEvent<PaymentSubmitPayload & { error: string }>) => void;
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Payment Methods
|
|
312
|
+
|
|
313
|
+
- **Card** - Credit and debit card payments
|
|
314
|
+
- **PSE** - Colombian online banking system
|
|
315
|
+
- **Cash** - Generate receipt for cash payment at physical locations
|
|
316
|
+
|
|
317
|
+
## License
|
|
318
|
+
|
|
319
|
+
[MIT](../../LICENSE)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import '@bloque/payments-elements';
|
|
2
|
+
import type { AppearanceConfig, CheckoutConfig, PaymentMethodType, PaymentSubmitPayload } from '@bloque/payments-elements';
|
|
3
|
+
declare global {
|
|
4
|
+
namespace JSX {
|
|
5
|
+
interface IntrinsicElements {
|
|
6
|
+
'bloque-checkout': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export interface BloqueCheckoutProps {
|
|
11
|
+
config?: CheckoutConfig;
|
|
12
|
+
appearance?: AppearanceConfig;
|
|
13
|
+
amount?: number;
|
|
14
|
+
availableMethods?: PaymentMethodType[];
|
|
15
|
+
requireEmail?: boolean;
|
|
16
|
+
showMethodSelector?: boolean;
|
|
17
|
+
onSubmit?: (payload: PaymentSubmitPayload) => Promise<void>;
|
|
18
|
+
onSuccess?: (event: CustomEvent<PaymentSubmitPayload>) => void;
|
|
19
|
+
onError?: (event: CustomEvent<PaymentSubmitPayload & {
|
|
20
|
+
error: string;
|
|
21
|
+
}>) => void;
|
|
22
|
+
className?: string;
|
|
23
|
+
style?: React.CSSProperties;
|
|
24
|
+
}
|
|
25
|
+
export declare function BloqueCheckout({ config, appearance, amount, availableMethods, requireEmail, showMethodSelector, onSubmit, onSuccess, onError, className, style, }: BloqueCheckoutProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import "@bloque/payments-elements";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
function BloqueCheckout({ config, appearance, amount, availableMethods, requireEmail = true, showMethodSelector = true, onSubmit, onSuccess, onError, className, style }) {
|
|
5
|
+
const checkoutRef = useRef(null);
|
|
6
|
+
useEffect(()=>{
|
|
7
|
+
const element = checkoutRef.current;
|
|
8
|
+
if (!element) return;
|
|
9
|
+
if (config) element.config = config;
|
|
10
|
+
if (appearance) element.appearance = appearance;
|
|
11
|
+
if (void 0 !== amount) element.amount = amount;
|
|
12
|
+
if (availableMethods) element.availableMethods = availableMethods;
|
|
13
|
+
element.requireEmail = requireEmail;
|
|
14
|
+
element.showMethodSelector = showMethodSelector;
|
|
15
|
+
if (onSubmit) element.onSubmit = onSubmit;
|
|
16
|
+
}, [
|
|
17
|
+
config,
|
|
18
|
+
appearance,
|
|
19
|
+
amount,
|
|
20
|
+
availableMethods,
|
|
21
|
+
requireEmail,
|
|
22
|
+
showMethodSelector,
|
|
23
|
+
onSubmit
|
|
24
|
+
]);
|
|
25
|
+
useEffect(()=>{
|
|
26
|
+
const element = checkoutRef.current;
|
|
27
|
+
if (!element) return;
|
|
28
|
+
const handleSuccess = (event)=>{
|
|
29
|
+
if (onSuccess) onSuccess(event);
|
|
30
|
+
};
|
|
31
|
+
const handleError = (event)=>{
|
|
32
|
+
if (onError) onError(event);
|
|
33
|
+
};
|
|
34
|
+
element.addEventListener('payment-success', handleSuccess);
|
|
35
|
+
element.addEventListener('payment-error', handleError);
|
|
36
|
+
return ()=>{
|
|
37
|
+
element.removeEventListener('payment-success', handleSuccess);
|
|
38
|
+
element.removeEventListener('payment-error', handleError);
|
|
39
|
+
};
|
|
40
|
+
}, [
|
|
41
|
+
onSuccess,
|
|
42
|
+
onError
|
|
43
|
+
]);
|
|
44
|
+
return /*#__PURE__*/ jsx("bloque-checkout", {
|
|
45
|
+
ref: checkoutRef,
|
|
46
|
+
className: className,
|
|
47
|
+
style: style
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export { BloqueCheckout };
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bloque/payments-react",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "React wrapper for Bloque payments web components",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"react",
|
|
22
|
+
"payments",
|
|
23
|
+
"checkout",
|
|
24
|
+
"bloque",
|
|
25
|
+
"colombia",
|
|
26
|
+
"pse",
|
|
27
|
+
"card",
|
|
28
|
+
"cash"
|
|
29
|
+
],
|
|
30
|
+
"author": "Nestor Cortina <nexckycort@gmail.com>",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public",
|
|
34
|
+
"provenance": true
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "rslib build",
|
|
38
|
+
"clean": "rm -rf node_modules && rm -rf dist",
|
|
39
|
+
"check": "biome check --write",
|
|
40
|
+
"dev": "rslib build --watch",
|
|
41
|
+
"storybook": "storybook dev",
|
|
42
|
+
"typecheck": "tsgo --noEmit"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@bloque/payments-elements": "workspace:*"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@rsbuild/core": "catalog:",
|
|
49
|
+
"@rsbuild/plugin-react": "^1.4.2",
|
|
50
|
+
"@rslib/core": "catalog:",
|
|
51
|
+
"@storybook/addon-docs": "^10.1.4",
|
|
52
|
+
"@storybook/addon-onboarding": "^10.1.4",
|
|
53
|
+
"@storybook/react": "^10.1.4",
|
|
54
|
+
"@types/react": "^19.2.7",
|
|
55
|
+
"@typescript/native-preview": "catalog:",
|
|
56
|
+
"react": "^19.2.1",
|
|
57
|
+
"storybook": "^10.1.4",
|
|
58
|
+
"storybook-addon-rslib": "^3.1.0",
|
|
59
|
+
"storybook-react-rsbuild": "^3.1.0",
|
|
60
|
+
"typescript": "catalog:"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"react": ">=16.9.0",
|
|
64
|
+
"react-dom": ">=16.9.0"
|
|
65
|
+
},
|
|
66
|
+
"homepage": "git+https://github.com/bloque-app/payments.git#readme",
|
|
67
|
+
"repository": {
|
|
68
|
+
"type": "git",
|
|
69
|
+
"url": "git+https://github.com/bloque-app/payments.git",
|
|
70
|
+
"directory": "packages/payments-react"
|
|
71
|
+
}
|
|
72
|
+
}
|