@rinnebr/js 0.1.0-alpha.1
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 +549 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +131 -0
- package/dist/index.js +1 -0
- package/package.json +78 -0
package/README.md
ADDED
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
# Rinne JS
|
|
2
|
+
|
|
3
|
+
A modern, type-safe browser library for integrating Apple Pay and Google Pay payment elements into your web applications. Built on Rinne's secure payment infrastructure with encryption powered by Evervault.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Rinne JS provides a simple, declarative API for rendering wallet payment buttons in the browser. The library handles:
|
|
8
|
+
|
|
9
|
+
- Secure payment token encryption and transmission
|
|
10
|
+
- Wallet button rendering and styling
|
|
11
|
+
- Payment flow orchestration
|
|
12
|
+
- Error handling and user cancellation
|
|
13
|
+
- Transaction management
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install rinne-js
|
|
19
|
+
# or
|
|
20
|
+
yarn add rinne-js
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { Rinne } from 'rinne-js'
|
|
27
|
+
|
|
28
|
+
// 1. Initialize Rinne with your merchant ID
|
|
29
|
+
const rinne = new Rinne({
|
|
30
|
+
merchantId: 'your-merchant-id',
|
|
31
|
+
environment: 'production' // or 'sandbox'
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// 2. Create a transaction
|
|
35
|
+
const transaction = await rinne.transaction.create({
|
|
36
|
+
amount: 1000, // Amount in cents
|
|
37
|
+
currency: 'BRL',
|
|
38
|
+
country: 'BR',
|
|
39
|
+
lineItems: [
|
|
40
|
+
{ label: 'Product Name', amount: 1000 }
|
|
41
|
+
]
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// 3. Create and mount an Apple Pay element
|
|
45
|
+
const applePay = await rinne.elements.applePay(transaction, {
|
|
46
|
+
button: {
|
|
47
|
+
color: 'black',
|
|
48
|
+
type: 'buy'
|
|
49
|
+
},
|
|
50
|
+
onCapture: async (payload, fail) => {
|
|
51
|
+
try {
|
|
52
|
+
// Send encrypted card data to your backend
|
|
53
|
+
await processPayment(payload.card_data, payload.transaction)
|
|
54
|
+
} catch (error) {
|
|
55
|
+
// Call fail() to notify the wallet dialog that payment processing failed
|
|
56
|
+
fail({ message: 'Payment processing failed' })
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
onError: (error) => {
|
|
60
|
+
console.error('Apple Pay error:', error)
|
|
61
|
+
},
|
|
62
|
+
onCancel: () => {
|
|
63
|
+
console.log('Payment cancelled by user')
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
await applePay.mount('#payment-button')
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Core Concepts
|
|
71
|
+
|
|
72
|
+
### Initialization
|
|
73
|
+
|
|
74
|
+
The `Rinne` class is the main entry point. It handles SDK initialization, merchant configuration fetching, and Evervault setup.
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const rinne = new Rinne({
|
|
78
|
+
merchantId: string // Required: Your Rinne merchant ID
|
|
79
|
+
environment?: Environment // Optional: 'sandbox' | 'production' (default: 'production')
|
|
80
|
+
rinneUrl?: string // Optional: Custom Rinne API base URL
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Lazy Initialization**: The SDK initializes asynchronously when you first create a transaction or payment element. This includes fetching merchant configuration and loading the Evervault SDK.
|
|
85
|
+
|
|
86
|
+
### Transactions
|
|
87
|
+
|
|
88
|
+
Every payment requires a transaction object that defines the payment amount, currency, and line items.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const transaction = await rinne.transaction.create({
|
|
92
|
+
amount: number // Required: Amount in cents (e.g., 1000 = $10.00)
|
|
93
|
+
currency: string // Required: ISO 4217 currency code (e.g., 'BRL', 'USD')
|
|
94
|
+
country?: string // Optional: ISO 3166 country code (e.g., 'BR', 'US')
|
|
95
|
+
lineItems?: Array<{ // Optional: Itemized list for wallet display
|
|
96
|
+
label: string // Line item description
|
|
97
|
+
amount: number // Line item amount in cents
|
|
98
|
+
}>
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Transaction objects are immutable and tied to a single payment attempt.
|
|
103
|
+
|
|
104
|
+
### Payment Elements
|
|
105
|
+
|
|
106
|
+
Payment elements are wallet-specific UI components that handle the complete payment flow:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// Apple Pay
|
|
110
|
+
const applePay = await rinne.elements.applePay(transaction, options)
|
|
111
|
+
await applePay.mount('#container')
|
|
112
|
+
|
|
113
|
+
// Google Pay
|
|
114
|
+
const googlePay = await rinne.elements.googlePay(transaction, options)
|
|
115
|
+
await googlePay.mount('#container')
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## API Reference
|
|
119
|
+
|
|
120
|
+
### `Rinne`
|
|
121
|
+
|
|
122
|
+
#### Constructor
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
new Rinne(options: Options)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Properties
|
|
129
|
+
|
|
130
|
+
- `transaction: TransactionNamespace` - Transaction management namespace
|
|
131
|
+
- `elements: ElementsNamespace` - Payment element creation namespace
|
|
132
|
+
|
|
133
|
+
### `rinne.transaction`
|
|
134
|
+
|
|
135
|
+
#### `create(params: TransactionCreateParams): Promise<EvTransaction>`
|
|
136
|
+
|
|
137
|
+
Creates a new transaction for a payment.
|
|
138
|
+
|
|
139
|
+
**Parameters:**
|
|
140
|
+
- `amount: number` - Payment amount in cents
|
|
141
|
+
- `currency: string` - ISO 4217 currency code
|
|
142
|
+
- `country?: string` - ISO 3166 country code
|
|
143
|
+
- `lineItems?: WalletLineItem[]` - Itemized breakdown
|
|
144
|
+
|
|
145
|
+
**Returns:** Transaction object to be used with payment elements
|
|
146
|
+
|
|
147
|
+
**Example:**
|
|
148
|
+
```typescript
|
|
149
|
+
const transaction = await rinne.transaction.create({
|
|
150
|
+
amount: 5000,
|
|
151
|
+
currency: 'BRL',
|
|
152
|
+
country: 'BR',
|
|
153
|
+
lineItems: [
|
|
154
|
+
{ label: 'Subtotal', amount: 4500 },
|
|
155
|
+
{ label: 'Shipping', amount: 500 }
|
|
156
|
+
]
|
|
157
|
+
})
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### `rinne.elements`
|
|
161
|
+
|
|
162
|
+
#### `applePay(transaction: EvTransaction, options: ApplePayMountOptions): Promise<ApplePayElement>`
|
|
163
|
+
|
|
164
|
+
Creates an Apple Pay payment element.
|
|
165
|
+
|
|
166
|
+
**Parameters:**
|
|
167
|
+
- `transaction` - Transaction object from `rinne.transaction.create()`
|
|
168
|
+
- `options` - Configuration and event handlers (see [Mount Options](#mount-options))
|
|
169
|
+
|
|
170
|
+
**Returns:** Apple Pay element with `mount()` method
|
|
171
|
+
|
|
172
|
+
#### `googlePay(transaction: EvTransaction, options: GooglePayMountOptions): Promise<GooglePayElement>`
|
|
173
|
+
|
|
174
|
+
Creates a Google Pay payment element.
|
|
175
|
+
|
|
176
|
+
**Parameters:**
|
|
177
|
+
- `transaction` - Transaction object from `rinne.transaction.create()`
|
|
178
|
+
- `options` - Configuration and event handlers (see [Mount Options](#mount-options))
|
|
179
|
+
|
|
180
|
+
**Returns:** Google Pay element with `mount()` method
|
|
181
|
+
|
|
182
|
+
### Payment Element Methods
|
|
183
|
+
|
|
184
|
+
#### `mount(target: string | HTMLElement): Promise<MountedElement>`
|
|
185
|
+
|
|
186
|
+
Mounts the payment button to a DOM element.
|
|
187
|
+
|
|
188
|
+
**Parameters:**
|
|
189
|
+
- `target` - CSS selector string (e.g., `'#button'`) or HTMLElement
|
|
190
|
+
|
|
191
|
+
**Returns:** Mounted element with `unmount()` method
|
|
192
|
+
|
|
193
|
+
**Throws:** If target element is not found or mounting fails
|
|
194
|
+
|
|
195
|
+
**Example:**
|
|
196
|
+
```typescript
|
|
197
|
+
const element = await rinne.elements.applePay(transaction, options)
|
|
198
|
+
|
|
199
|
+
// Mount using selector
|
|
200
|
+
await element.mount('#apple-pay-button')
|
|
201
|
+
|
|
202
|
+
// Or mount using element reference
|
|
203
|
+
const container = document.getElementById('payment-container')
|
|
204
|
+
await element.mount(container)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### `unmount(): void`
|
|
208
|
+
|
|
209
|
+
Removes the payment button from the DOM.
|
|
210
|
+
|
|
211
|
+
**Example:**
|
|
212
|
+
```typescript
|
|
213
|
+
const mounted = await element.mount('#container')
|
|
214
|
+
|
|
215
|
+
// Later...
|
|
216
|
+
mounted.unmount()
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Configuration
|
|
220
|
+
|
|
221
|
+
### Mount Options
|
|
222
|
+
|
|
223
|
+
Both `applePay()` and `googlePay()` accept the same configuration structure:
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
interface MountOptions {
|
|
227
|
+
button?: WalletButtonOptions
|
|
228
|
+
onCapture?: (payload: WalletSuccessPayload, fail: (error) => void) => void
|
|
229
|
+
onError?: (error: WalletError) => void
|
|
230
|
+
onCancel?: () => void
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Button Options
|
|
235
|
+
|
|
236
|
+
Customize the appearance of wallet buttons:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
interface WalletButtonOptions {
|
|
240
|
+
color?: 'black' | 'white' // Default: 'black'
|
|
241
|
+
locale?: 'pt' | 'en' | 'es' // Default: 'pt'
|
|
242
|
+
type?: WalletButtonType // Default: 'plain'
|
|
243
|
+
borderRadius?: number // Default: platform-specific
|
|
244
|
+
size?: {
|
|
245
|
+
width: number | string // Default: '100%'
|
|
246
|
+
height: number | string // Default: '40px'
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Button Types:**
|
|
252
|
+
- `'plain'` - Generic payment button
|
|
253
|
+
- `'buy'` - "Buy with [Wallet]"
|
|
254
|
+
- `'book'` - "Book with [Wallet]"
|
|
255
|
+
- `'checkout'` - "Check out with [Wallet]"
|
|
256
|
+
- `'donate'` - "Donate with [Wallet]"
|
|
257
|
+
- `'order'` - "Order with [Wallet]"
|
|
258
|
+
- `'pay'` - "Pay with [Wallet]"
|
|
259
|
+
- `'subscribe'` - "Subscribe with [Wallet]"
|
|
260
|
+
|
|
261
|
+
**Defaults:**
|
|
262
|
+
```typescript
|
|
263
|
+
{
|
|
264
|
+
color: 'black',
|
|
265
|
+
locale: 'pt',
|
|
266
|
+
type: 'plain',
|
|
267
|
+
size: {
|
|
268
|
+
width: '100%',
|
|
269
|
+
height: '40px'
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Example:**
|
|
275
|
+
```typescript
|
|
276
|
+
const applePay = await rinne.elements.applePay(transaction, {
|
|
277
|
+
button: {
|
|
278
|
+
color: 'white',
|
|
279
|
+
locale: 'en',
|
|
280
|
+
type: 'buy',
|
|
281
|
+
borderRadius: 8,
|
|
282
|
+
size: {
|
|
283
|
+
width: '300px',
|
|
284
|
+
height: '48px'
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
})
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Event Handlers
|
|
291
|
+
|
|
292
|
+
### `onCapture`
|
|
293
|
+
|
|
294
|
+
Called when the user successfully authorizes payment with their wallet. This is where you process the payment on your backend.
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
onCapture: (payload: WalletSuccessPayload, fail: (error) => void) => void
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**Parameters:**
|
|
301
|
+
|
|
302
|
+
1. **`payload: WalletSuccessPayload`**
|
|
303
|
+
```typescript
|
|
304
|
+
{
|
|
305
|
+
card_data: CardData // Encrypted payment token and card details
|
|
306
|
+
transaction: EvTransaction // Original transaction object
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
**CardData structure:**
|
|
311
|
+
```typescript
|
|
312
|
+
{
|
|
313
|
+
cryptogram: string // Payment cryptogram for authorization
|
|
314
|
+
expiry_month: string // Card expiry month
|
|
315
|
+
expiry_year: string // Card expiry year
|
|
316
|
+
wallet_type: 'APPLE_PAY' | 'GOOGLE_PAY'
|
|
317
|
+
last_digits?: string // Last 4 digits of card
|
|
318
|
+
brand?: string // Card brand (Visa, Mastercard, etc.)
|
|
319
|
+
network_token?: string // Network tokenized PAN
|
|
320
|
+
device_id?: string // Device identifier
|
|
321
|
+
eci?: string // E-commerce indicator
|
|
322
|
+
// ... additional fields
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
2. **`fail: (error: { message: string }) => void`**
|
|
327
|
+
|
|
328
|
+
**CRITICAL**: This callback must be called if payment processing fails on your backend. It notifies the wallet dialog to show an error state to the user and allows them to retry or cancel.
|
|
329
|
+
|
|
330
|
+
If you don't call `fail()` after a processing error, the wallet dialog might appear stuck in a processing state or incorrectly report success.
|
|
331
|
+
|
|
332
|
+
**Example:**
|
|
333
|
+
```typescript
|
|
334
|
+
onCapture: async (payload, fail) => {
|
|
335
|
+
try {
|
|
336
|
+
// Send encrypted card data to your backend
|
|
337
|
+
const response = await fetch('/api/process-payment', {
|
|
338
|
+
method: 'POST',
|
|
339
|
+
headers: { 'Content-Type': 'application/json' },
|
|
340
|
+
body: JSON.stringify({
|
|
341
|
+
cardData: payload.card_data,
|
|
342
|
+
transactionId: payload.transaction.id
|
|
343
|
+
})
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
if (!response.ok) {
|
|
347
|
+
throw new Error('Payment processing failed')
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Payment succeeded - wallet dialog will close automatically
|
|
351
|
+
const result = await response.json()
|
|
352
|
+
console.log('Payment successful:', result)
|
|
353
|
+
|
|
354
|
+
} catch (error) {
|
|
355
|
+
// Payment failed - MUST call fail() to notify the wallet
|
|
356
|
+
fail({
|
|
357
|
+
message: error.message || 'Unable to process payment'
|
|
358
|
+
})
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### `onError`
|
|
364
|
+
|
|
365
|
+
Called when an error occurs during the wallet handling (e.g., wallet unavailable, network error, configuration error).
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
onError: (error: WalletError) => void
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
**Error structure:**
|
|
372
|
+
```typescript
|
|
373
|
+
{
|
|
374
|
+
code: 'PROVIDER_ERROR' | 'VALIDATION_ERROR' | 'CONFIGURATION_ERROR' | 'UNKNOWN'
|
|
375
|
+
message: string
|
|
376
|
+
details?: unknown
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**Error codes:**
|
|
381
|
+
- `PROVIDER_ERROR` - Error from Apple Pay or Google Pay provider
|
|
382
|
+
- `VALIDATION_ERROR` - Invalid configuration or parameters
|
|
383
|
+
- `CONFIGURATION_ERROR` - Merchant configuration issue
|
|
384
|
+
- `UNKNOWN` - Unexpected error
|
|
385
|
+
|
|
386
|
+
**Example:**
|
|
387
|
+
```typescript
|
|
388
|
+
onError: (error) => {
|
|
389
|
+
console.error('Payment error:', error)
|
|
390
|
+
|
|
391
|
+
if (error.code === 'PROVIDER_ERROR') {
|
|
392
|
+
alert('Payment service unavailable. Please try another payment method.')
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### `onCancel`
|
|
398
|
+
|
|
399
|
+
Called when the user explicitly cancels the payment (closes the wallet dialog without authorizing).
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
onCancel: () => void
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Example:**
|
|
406
|
+
```typescript
|
|
407
|
+
onCancel: () => {
|
|
408
|
+
console.log('User cancelled payment')
|
|
409
|
+
// Optionally re-enable other UI elements
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
## Complete Example
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
import { Rinne } from 'rinne-js'
|
|
417
|
+
|
|
418
|
+
const rinne = new Rinne({
|
|
419
|
+
merchantId: 'merchant_abc123',
|
|
420
|
+
environment: 'production'
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
async function setupPayment() {
|
|
424
|
+
// Create transaction
|
|
425
|
+
const transaction = await rinne.transaction.create({
|
|
426
|
+
amount: 2500, // $25.00
|
|
427
|
+
currency: 'USD',
|
|
428
|
+
country: 'US',
|
|
429
|
+
lineItems: [
|
|
430
|
+
{ label: 'Premium Subscription', amount: 2000 },
|
|
431
|
+
{ label: 'Tax', amount: 500 }
|
|
432
|
+
]
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
// Setup Apple Pay
|
|
436
|
+
const applePay = await rinne.elements.applePay(transaction, {
|
|
437
|
+
button: {
|
|
438
|
+
color: 'black',
|
|
439
|
+
type: 'subscribe',
|
|
440
|
+
locale: 'en',
|
|
441
|
+
size: {
|
|
442
|
+
width: '100%',
|
|
443
|
+
height: '48px'
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
onCapture: async (payload, fail) => {
|
|
447
|
+
try {
|
|
448
|
+
const response = await fetch('/api/payments', {
|
|
449
|
+
method: 'POST',
|
|
450
|
+
headers: { 'Content-Type': 'application/json' },
|
|
451
|
+
body: JSON.stringify({
|
|
452
|
+
cardData: payload.card_data,
|
|
453
|
+
transactionId: payload.transaction.id
|
|
454
|
+
})
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
if (!response.ok) {
|
|
458
|
+
const error = await response.json()
|
|
459
|
+
throw new Error(error.message)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Payment successful
|
|
463
|
+
window.location.href = '/success'
|
|
464
|
+
|
|
465
|
+
} catch (error) {
|
|
466
|
+
// Notify wallet of failure
|
|
467
|
+
fail({ message: error.message })
|
|
468
|
+
|
|
469
|
+
// Show error to user
|
|
470
|
+
alert('Payment failed: ' + error.message)
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
onError: (error) => {
|
|
474
|
+
console.error('Payment error:', error)
|
|
475
|
+
alert('Payment system error. Please try again.')
|
|
476
|
+
},
|
|
477
|
+
onCancel: () => {
|
|
478
|
+
console.log('Payment cancelled')
|
|
479
|
+
}
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
await applePay.mount('#apple-pay-button')
|
|
483
|
+
|
|
484
|
+
// Setup Google Pay
|
|
485
|
+
const googlePay = await rinne.elements.googlePay(transaction, {
|
|
486
|
+
button: {
|
|
487
|
+
color: 'black',
|
|
488
|
+
type: 'subscribe',
|
|
489
|
+
locale: 'en'
|
|
490
|
+
},
|
|
491
|
+
onCapture: async (payload, fail) => {
|
|
492
|
+
// Same handler as Apple Pay
|
|
493
|
+
try {
|
|
494
|
+
await processPayment(payload)
|
|
495
|
+
} catch (error) {
|
|
496
|
+
fail({ message: error.message })
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
onError: (error) => console.error(error),
|
|
500
|
+
onCancel: () => console.log('Cancelled')
|
|
501
|
+
})
|
|
502
|
+
|
|
503
|
+
await googlePay.mount('#google-pay-button')
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
setupPayment()
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
## TypeScript Support
|
|
510
|
+
|
|
511
|
+
Rinne JS is written in TypeScript and exports all type definitions:
|
|
512
|
+
|
|
513
|
+
```typescript
|
|
514
|
+
import type {
|
|
515
|
+
Options,
|
|
516
|
+
Environment,
|
|
517
|
+
TransactionCreateParams,
|
|
518
|
+
WalletSuccessPayload,
|
|
519
|
+
WalletError,
|
|
520
|
+
WalletButtonOptions,
|
|
521
|
+
CardData,
|
|
522
|
+
ApplePayElement,
|
|
523
|
+
GooglePayElement,
|
|
524
|
+
MountedElement
|
|
525
|
+
} from 'rinne-js'
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Error Handling Best Practices
|
|
529
|
+
|
|
530
|
+
1. **Always handle `onCapture` failures**: Call `fail()` with a useful message if backend processing fails
|
|
531
|
+
2. **Implement `onError`**: Handle what to do in the rare event that the wallet buttons fail to load
|
|
532
|
+
3. **Handle `onCancel`**: Clean up UI state when user cancels
|
|
533
|
+
4. **Network errors**: Wrap backend calls in try-catch
|
|
534
|
+
5. **Validation**: Validate transaction parameters before calling `transaction.create()`
|
|
535
|
+
|
|
536
|
+
## Security
|
|
537
|
+
|
|
538
|
+
- All payment tokens are encrypted using Evervault before transmission
|
|
539
|
+
- No sensitive card data is stored in browser memory
|
|
540
|
+
- Source maps are disabled to protect payment flow logic
|
|
541
|
+
- Strict Content Security Policy recommended
|
|
542
|
+
|
|
543
|
+
## Development
|
|
544
|
+
|
|
545
|
+
See [CLAUDE.md](./CLAUDE.md) for development setup and tooling information.
|
|
546
|
+
|
|
547
|
+
## License
|
|
548
|
+
|
|
549
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let e=null;async function o(){if(window.Evervault)return window.Evervault;try{return await async function(){return e||(e=new Promise((o,r)=>{const t=document.createElement("script");if(t.src="https://js.evervault.com/v2",t.onload=()=>o(),t.onerror=()=>{e=null,r()},!document.head)throw new Error("Expected document.head not to be null. Evervault.js requires a <head> element.");document.head.appendChild(t)}),e)}(),window.Evervault}catch{throw new Error("Failed to load Evervault.js")}}Promise.resolve().then(()=>{o()});const r={pt:{errors:{targetElementNotFound:"Elemento alvo não encontrado",failedToInitialize:"Falha ao inicializar Rinne SDK",failedToProcessApplePay:"Falha ao processar dados do Apple Pay",failedToProcessGooglePay:"Falha ao processar dados do Google Pay",applePayError:"Erro do Apple Pay",googlePayError:"Erro do Google Pay",failedToMountApplePay:"Falha ao montar botão do Apple Pay",failedToMountGooglePay:"Falha ao montar botão do Google Pay",failedToFetchConfig:"Falha ao buscar configuração do comerciante",invalidConfig:"Configuração de comerciante inválida: campos obrigatórios ausentes",configFetchFailed:"Falha ao buscar configuração",unknownError:"Erro desconhecido"}},en:{errors:{targetElementNotFound:"Target element not found",failedToInitialize:"Failed to initialize Rinne SDK",failedToProcessApplePay:"Failed to process Apple Pay data",failedToProcessGooglePay:"Failed to process Google Pay data",applePayError:"Apple Pay error occurred",googlePayError:"Google Pay error occurred",failedToMountApplePay:"Failed to mount Apple Pay button",failedToMountGooglePay:"Failed to mount Google Pay button",failedToFetchConfig:"Failed to fetch merchant config",invalidConfig:"Invalid merchant config: missing required fields",configFetchFailed:"Configuration fetch failed",unknownError:"Unknown error"}},es:{errors:{targetElementNotFound:"Elemento objetivo no encontrado",failedToInitialize:"Error al inicializar Rinne SDK",failedToProcessApplePay:"Error al procesar datos de Apple Pay",failedToProcessGooglePay:"Error al procesar datos de Google Pay",applePayError:"Error de Apple Pay",googlePayError:"Error de Google Pay",failedToMountApplePay:"Error al montar botón de Apple Pay",failedToMountGooglePay:"Error al montar botón de Google Pay",failedToFetchConfig:"Error al obtener configuración del comerciante",invalidConfig:"Configuración de comerciante inválida: faltan campos requeridos",configFetchFailed:"Error al obtener configuración",unknownError:"Error desconocido"}}};function t(e="pt"){return r[e]}const n={sandbox:"https://api-sandbox.rinne.com.br/core",production:"https://api.rinne.com.br/core"};function a(e){return String(e).padStart(2,"0")}function i(e){const o=String(e);return 2===o.length?`20${o}`:o}function s(e){if(void 0!==e)return 0===e?"0":e}function c(e){return{pt:"pt-BR",en:"en-US",es:"es-ES"}[e]}function l(e,o="pt"){if("string"==typeof e){const r=document.querySelector(e);if(!r){const r=t(o).errors.targetElementNotFound;throw new Error(`${r}: ${e}`)}return r}return e}function d(e,o,r){return{code:e,message:o,details:r}}function u(e,o){return(o,r)=>{let n=!1,u=!1;const p=r.button?.locale??"pt",h=t(p),y=r.button?.size??{width:"100%",height:"40px"},{width:m,height:f}=y,g={locale:c(r.button?.locale??"pt"),type:(v=r.button?.type??"plain",{book:"book",buy:"buy",checkout:"check-out",donate:"donate",order:"order",pay:"pay",plain:"plain",subscribe:"subscribe"}[v]),style:(P=r.button?.color??"black","white"===P?"white-outline":P),borderRadius:s(r.button?.borderRadius),...!!m&&!!f&&{size:{width:m,height:f}},process:async(e,t)=>{if(u||n)return Promise.resolve();try{n=!0;const s=function(e){const{networkToken:o,card:r,cryptogram:t,eci:n,paymentDataType:s,deviceManufacturerIdentifier:c}=e;return{network_token:o.number,expiry_month:a(o.expiry.month),expiry_year:i(o.expiry.year),cryptogram:t,eci:n,token_provider:o.tokenServiceProvider,brand:r.brand,device_id:c,authentication_type:"3DSecure"===s?"3DS":void 0,last_digits:r.lastFour,issuer:"issuer"in r?r.issuer:void 0,segment:"segment"in r?r.segment:void 0,country:"country"in r?r.country:void 0,wallet_type:"APPLE_PAY",display_name:r.displayName}}(e);let c=!1;const l=e=>{c=!0,t.fail(e)};return await(r.onCapture?.({card_data:s,transaction:o},l)),void(c||(u=!0))}catch(s){return t.fail({message:s instanceof Error?s.message:h.errors.failedToProcessApplePay}),Promise.resolve()}finally{u||(n=!1)}}};var P,v;const E=e.ui.applePay(o,g);return E.on("error",(...e)=>{if(n)return;n=!0;const o="string"==typeof e[0]?e[0]:void 0;if("CANCELLED"===o?.toUpperCase())return n=!1,void r.onCancel?.();const t={code:"PROVIDER_ERROR",message:o??h.errors.applePayError};r.onError?.(t)}),E.on("cancel",()=>{n=!1,r.onCancel?.()}),Promise.resolve({async mount(e){try{const o=l(e,p);return await E.mount(o),{unmount:()=>{E.unmount()}}}catch(o){const e=d("PROVIDER_ERROR",o instanceof Error?o.message:h.errors.failedToMountApplePay,o);throw r.onError&&r.onError(e),o}}})}}function p(e,o){return(o,r)=>{let n=!1,c=!1;const u=r.button?.locale??"pt",p=t(u),h=r.button?.size??{width:"100%",height:"40px"},{width:y,height:m}=h,f={locale:r.button?.locale??"pt",type:r.button?.type??"plain",color:r.button?.color??"black",borderRadius:s(r.button?.borderRadius),...!!y&&!!m&&{size:{width:y,height:m}},allowedAuthMethods:["CRYPTOGRAM_3DS"],process:async(e,t)=>{if(c||n)return Promise.resolve();try{n=!0;const s=function(e){if(!function(e){return"token"in e&&"tokenServiceProvider"in e.token}(e))throw new Error("Google Pay response must contain a network token (3DS authentication)");const{token:o,card:r,cryptogram:t,eci:n}=e;return{network_token:o.number,expiry_month:a(o.expiry.month),expiry_year:i(o.expiry.year),cryptogram:t,eci:n??null,token_provider:o.tokenServiceProvider,brand:r.brand,authentication_type:"3DS",last_digits:r.lastFour,issuer:"issuer"in r?r.issuer:void 0,segment:"segment"in r?r.segment:void 0,country:"country"in r?r.country:void 0,wallet_type:"GOOGLE_PAY",display_name:r.displayName}}(e);let l=!1;const d=e=>{l=!0,t.fail(e)};return await(r.onCapture?.({card_data:s,transaction:o},d)),void(l||(c=!0))}catch(s){return t.fail({message:s instanceof Error?s.message:p.errors.failedToProcessGooglePay}),Promise.resolve()}finally{c||(n=!1)}}},g=e.ui.googlePay(o,f);return g.on("error",(...e)=>{if(n)return;n=!0;const o={code:"PROVIDER_ERROR",message:("string"==typeof e[0]?e[0]:void 0)??p.errors.googlePayError};r.onError?.(o)}),g.on("cancel",()=>{n=!1,r.onCancel?.()}),Promise.resolve({mount(e){try{const o=l(e,u);return g.mount(o),Promise.resolve({unmount:()=>{g.unmount()}})}catch(o){const e=d("PROVIDER_ERROR",o instanceof Error?o.message:p.errors.failedToMountGooglePay,o);throw r.onError&&r.onError(e),o}}})}}exports.Rinne=class{constructor(e){this.options=e,this.transaction=this.createTransactionNamespace(),this.elements=this.createElementsNamespace()}config=null;evervault=null;initPromise=null;transaction;elements;async initialize(){return this.initPromise||(this.initPromise=(async()=>{try{const e=await async function(e){const o=t("pt"),r=`${e.baseUrl??n[e.environment??"production"]}/merchants/${e.merchantId}/public-settings`;try{const e=await fetch(r);if(!e.ok){const r=String(e.status);throw new Error(`${o.errors.failedToFetchConfig}: ${r} ${e.statusText}`)}const t=await e.json();if(!t.merchant_id||!t.team_id||!t.app_id)throw new Error(o.errors.invalidConfig);return{merchantId:t.merchant_id,teamId:t.team_id,appId:t.app_id,availableMethods:t.available_methods,name:t.name}}catch(a){if(a instanceof Error)throw new Error(`${o.errors.configFetchFailed}: ${a.message}`);throw new Error(`${o.errors.configFetchFailed}: ${o.errors.unknownError}`)}}({merchantId:this.options.merchantId,environment:this.options.environment??"production",baseUrl:this.options.rinneUrl});this.config=e,this.evervault=await async function(e,r,t){return new(await o())(e,r,t)}(this.config.teamId,this.config.appId)}catch(e){throw this.initPromise=null,e}})()),this.initPromise}createElementsNamespace(){return{applePay:async(e,o)=>{if(await this.initialize(),!this.evervault||!this.config){const e=t(o.button?.locale??"pt");throw new Error(e.errors.failedToInitialize)}return u(this.evervault,this.config)(e,o)},googlePay:async(e,o)=>{if(await this.initialize(),!this.evervault||!this.config){const e=t(o.button?.locale??"pt");throw new Error(e.errors.failedToInitialize)}return p(this.evervault,this.config)(e,o)}}}createTransactionNamespace(){return{create:async e=>{if(await this.initialize(),!this.evervault||!this.config?.merchantId){const e=t("pt");throw new Error(e.errors.failedToInitialize)}return this.evervault.transactions.create({type:"payment",...e,merchantId:this.config.merchantId})}}}};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { EvTransaction } from './evervault-types';
|
|
2
|
+
|
|
3
|
+
export declare interface ApplePayElement {
|
|
4
|
+
mount(target: string | HTMLElement): Promise<MountedElement>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export declare type ApplePayElementNamespace = (transaction: EvTransaction, options: ApplePayMountOptions) => Promise<ApplePayElement>;
|
|
8
|
+
|
|
9
|
+
export declare interface ApplePayMountOptions extends WalletHandlers {
|
|
10
|
+
button?: WalletButtonOptions;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export declare interface CardData {
|
|
14
|
+
number?: string;
|
|
15
|
+
network_token?: string;
|
|
16
|
+
expiry_month: string;
|
|
17
|
+
expiry_year: string;
|
|
18
|
+
cvv?: string;
|
|
19
|
+
cryptogram: string;
|
|
20
|
+
eci?: string | null;
|
|
21
|
+
token_provider?: string;
|
|
22
|
+
brand?: string;
|
|
23
|
+
device_id?: string;
|
|
24
|
+
authentication_type?: string;
|
|
25
|
+
last_digits?: string;
|
|
26
|
+
issuer?: string;
|
|
27
|
+
segment?: string;
|
|
28
|
+
country?: string;
|
|
29
|
+
wallet_type?: WalletType;
|
|
30
|
+
display_name?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export declare interface ElementsNamespace {
|
|
34
|
+
applePay: ApplePayElementNamespace;
|
|
35
|
+
googlePay: GooglePayElementNamespace;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export declare type Environment = 'sandbox' | 'production';
|
|
39
|
+
|
|
40
|
+
export { EvTransaction }
|
|
41
|
+
|
|
42
|
+
export declare interface GooglePayElement {
|
|
43
|
+
mount(target: string | HTMLElement): Promise<MountedElement>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export declare type GooglePayElementNamespace = (transaction: EvTransaction, options: GooglePayMountOptions) => Promise<GooglePayElement>;
|
|
47
|
+
|
|
48
|
+
export declare interface GooglePayMountOptions extends WalletHandlers {
|
|
49
|
+
button?: WalletButtonOptions;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export declare interface MountedElement {
|
|
53
|
+
unmount(): void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export declare interface Options {
|
|
57
|
+
merchantId: string;
|
|
58
|
+
environment?: Environment;
|
|
59
|
+
rinneUrl?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export declare class Rinne {
|
|
63
|
+
private options;
|
|
64
|
+
private config;
|
|
65
|
+
private evervault;
|
|
66
|
+
private initPromise;
|
|
67
|
+
readonly transaction: TransactionNamespace;
|
|
68
|
+
readonly elements: ElementsNamespace;
|
|
69
|
+
constructor(options: Options);
|
|
70
|
+
private initialize;
|
|
71
|
+
private createElementsNamespace;
|
|
72
|
+
private createTransactionNamespace;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export declare interface TransactionCreateParams {
|
|
76
|
+
amount: number;
|
|
77
|
+
currency: string;
|
|
78
|
+
country?: string;
|
|
79
|
+
lineItems?: WalletLineItem[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export declare interface TransactionNamespace {
|
|
83
|
+
create(params: TransactionCreateParams): Promise<EvTransaction>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
declare type WalletButtonColor = 'black' | 'white';
|
|
87
|
+
|
|
88
|
+
export declare interface WalletButtonOptions {
|
|
89
|
+
color?: WalletButtonColor;
|
|
90
|
+
locale?: WalletLocale;
|
|
91
|
+
type?: WalletButtonType;
|
|
92
|
+
borderRadius?: number;
|
|
93
|
+
size?: {
|
|
94
|
+
width: number | string;
|
|
95
|
+
height: number | string;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export declare type WalletButtonType = 'book' | 'buy' | 'checkout' | 'donate' | 'order' | 'pay' | 'plain' | 'subscribe';
|
|
100
|
+
|
|
101
|
+
export declare interface WalletError {
|
|
102
|
+
code: WalletErrorCode;
|
|
103
|
+
message: string;
|
|
104
|
+
details?: unknown;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export declare type WalletErrorCode = 'PROVIDER_ERROR' | 'VALIDATION_ERROR' | 'CONFIGURATION_ERROR' | 'UNKNOWN';
|
|
108
|
+
|
|
109
|
+
export declare interface WalletHandlers {
|
|
110
|
+
onCapture?: (payload: WalletSuccessPayload, fail: (error: {
|
|
111
|
+
message: string;
|
|
112
|
+
}) => void) => Promise<void> | void;
|
|
113
|
+
onError?: (error: WalletError) => void;
|
|
114
|
+
onCancel?: () => void;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export declare interface WalletLineItem {
|
|
118
|
+
amount: number;
|
|
119
|
+
label: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export declare type WalletLocale = 'pt' | 'en' | 'es';
|
|
123
|
+
|
|
124
|
+
export declare interface WalletSuccessPayload {
|
|
125
|
+
card_data: CardData;
|
|
126
|
+
transaction: EvTransaction;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export declare type WalletType = 'APPLE_PAY' | 'GOOGLE_PAY';
|
|
130
|
+
|
|
131
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
let e=null;async function o(){if(window.Evervault)return window.Evervault;try{return await async function(){return e||(e=new Promise((o,r)=>{const t=document.createElement("script");if(t.src="https://js.evervault.com/v2",t.onload=()=>o(),t.onerror=()=>{e=null,r()},!document.head)throw new Error("Expected document.head not to be null. Evervault.js requires a <head> element.");document.head.appendChild(t)}),e)}(),window.Evervault}catch{throw new Error("Failed to load Evervault.js")}}Promise.resolve().then(()=>{o()});const r={pt:{errors:{targetElementNotFound:"Elemento alvo não encontrado",failedToInitialize:"Falha ao inicializar Rinne SDK",failedToProcessApplePay:"Falha ao processar dados do Apple Pay",failedToProcessGooglePay:"Falha ao processar dados do Google Pay",applePayError:"Erro do Apple Pay",googlePayError:"Erro do Google Pay",failedToMountApplePay:"Falha ao montar botão do Apple Pay",failedToMountGooglePay:"Falha ao montar botão do Google Pay",failedToFetchConfig:"Falha ao buscar configuração do comerciante",invalidConfig:"Configuração de comerciante inválida: campos obrigatórios ausentes",configFetchFailed:"Falha ao buscar configuração",unknownError:"Erro desconhecido"}},en:{errors:{targetElementNotFound:"Target element not found",failedToInitialize:"Failed to initialize Rinne SDK",failedToProcessApplePay:"Failed to process Apple Pay data",failedToProcessGooglePay:"Failed to process Google Pay data",applePayError:"Apple Pay error occurred",googlePayError:"Google Pay error occurred",failedToMountApplePay:"Failed to mount Apple Pay button",failedToMountGooglePay:"Failed to mount Google Pay button",failedToFetchConfig:"Failed to fetch merchant config",invalidConfig:"Invalid merchant config: missing required fields",configFetchFailed:"Configuration fetch failed",unknownError:"Unknown error"}},es:{errors:{targetElementNotFound:"Elemento objetivo no encontrado",failedToInitialize:"Error al inicializar Rinne SDK",failedToProcessApplePay:"Error al procesar datos de Apple Pay",failedToProcessGooglePay:"Error al procesar datos de Google Pay",applePayError:"Error de Apple Pay",googlePayError:"Error de Google Pay",failedToMountApplePay:"Error al montar botón de Apple Pay",failedToMountGooglePay:"Error al montar botón de Google Pay",failedToFetchConfig:"Error al obtener configuración del comerciante",invalidConfig:"Configuración de comerciante inválida: faltan campos requeridos",configFetchFailed:"Error al obtener configuración",unknownError:"Error desconocido"}}};function t(e="pt"){return r[e]}const n={sandbox:"https://api-sandbox.rinne.com.br/core",production:"https://api.rinne.com.br/core"};function a(e){return String(e).padStart(2,"0")}function i(e){const o=String(e);return 2===o.length?`20${o}`:o}function s(e){if(void 0!==e)return 0===e?"0":e}function c(e){return{pt:"pt-BR",en:"en-US",es:"es-ES"}[e]}function l(e,o="pt"){if("string"==typeof e){const r=document.querySelector(e);if(!r){const r=t(o).errors.targetElementNotFound;throw new Error(`${r}: ${e}`)}return r}return e}function d(e,o,r){return{code:e,message:o,details:r}}function u(e,o){return(o,r)=>{let n=!1,u=!1;const p=r.button?.locale??"pt",h=t(p),m=r.button?.size??{width:"100%",height:"40px"},{width:y,height:f}=m,g={locale:c(r.button?.locale??"pt"),type:(v=r.button?.type??"plain",{book:"book",buy:"buy",checkout:"check-out",donate:"donate",order:"order",pay:"pay",plain:"plain",subscribe:"subscribe"}[v]),style:(P=r.button?.color??"black","white"===P?"white-outline":P),borderRadius:s(r.button?.borderRadius),...!!y&&!!f&&{size:{width:y,height:f}},process:async(e,t)=>{if(u||n)return Promise.resolve();try{n=!0;const s=function(e){const{networkToken:o,card:r,cryptogram:t,eci:n,paymentDataType:s,deviceManufacturerIdentifier:c}=e;return{network_token:o.number,expiry_month:a(o.expiry.month),expiry_year:i(o.expiry.year),cryptogram:t,eci:n,token_provider:o.tokenServiceProvider,brand:r.brand,device_id:c,authentication_type:"3DSecure"===s?"3DS":void 0,last_digits:r.lastFour,issuer:"issuer"in r?r.issuer:void 0,segment:"segment"in r?r.segment:void 0,country:"country"in r?r.country:void 0,wallet_type:"APPLE_PAY",display_name:r.displayName}}(e);let c=!1;const l=e=>{c=!0,t.fail(e)};return await(r.onCapture?.({card_data:s,transaction:o},l)),void(c||(u=!0))}catch(s){return t.fail({message:s instanceof Error?s.message:h.errors.failedToProcessApplePay}),Promise.resolve()}finally{u||(n=!1)}}};var P,v;const E=e.ui.applePay(o,g);return E.on("error",(...e)=>{if(n)return;n=!0;const o="string"==typeof e[0]?e[0]:void 0;if("CANCELLED"===o?.toUpperCase())return n=!1,void r.onCancel?.();const t={code:"PROVIDER_ERROR",message:o??h.errors.applePayError};r.onError?.(t)}),E.on("cancel",()=>{n=!1,r.onCancel?.()}),Promise.resolve({async mount(e){try{const o=l(e,p);return await E.mount(o),{unmount:()=>{E.unmount()}}}catch(o){const e=d("PROVIDER_ERROR",o instanceof Error?o.message:h.errors.failedToMountApplePay,o);throw r.onError&&r.onError(e),o}}})}}function p(e,o){return(o,r)=>{let n=!1,c=!1;const u=r.button?.locale??"pt",p=t(u),h=r.button?.size??{width:"100%",height:"40px"},{width:m,height:y}=h,f={locale:r.button?.locale??"pt",type:r.button?.type??"plain",color:r.button?.color??"black",borderRadius:s(r.button?.borderRadius),...!!m&&!!y&&{size:{width:m,height:y}},allowedAuthMethods:["CRYPTOGRAM_3DS"],process:async(e,t)=>{if(c||n)return Promise.resolve();try{n=!0;const s=function(e){if(!function(e){return"token"in e&&"tokenServiceProvider"in e.token}(e))throw new Error("Google Pay response must contain a network token (3DS authentication)");const{token:o,card:r,cryptogram:t,eci:n}=e;return{network_token:o.number,expiry_month:a(o.expiry.month),expiry_year:i(o.expiry.year),cryptogram:t,eci:n??null,token_provider:o.tokenServiceProvider,brand:r.brand,authentication_type:"3DS",last_digits:r.lastFour,issuer:"issuer"in r?r.issuer:void 0,segment:"segment"in r?r.segment:void 0,country:"country"in r?r.country:void 0,wallet_type:"GOOGLE_PAY",display_name:r.displayName}}(e);let l=!1;const d=e=>{l=!0,t.fail(e)};return await(r.onCapture?.({card_data:s,transaction:o},d)),void(l||(c=!0))}catch(s){return t.fail({message:s instanceof Error?s.message:p.errors.failedToProcessGooglePay}),Promise.resolve()}finally{c||(n=!1)}}},g=e.ui.googlePay(o,f);return g.on("error",(...e)=>{if(n)return;n=!0;const o={code:"PROVIDER_ERROR",message:("string"==typeof e[0]?e[0]:void 0)??p.errors.googlePayError};r.onError?.(o)}),g.on("cancel",()=>{n=!1,r.onCancel?.()}),Promise.resolve({mount(e){try{const o=l(e,u);return g.mount(o),Promise.resolve({unmount:()=>{g.unmount()}})}catch(o){const e=d("PROVIDER_ERROR",o instanceof Error?o.message:p.errors.failedToMountGooglePay,o);throw r.onError&&r.onError(e),o}}})}}class h{constructor(e){this.options=e,this.transaction=this.createTransactionNamespace(),this.elements=this.createElementsNamespace()}config=null;evervault=null;initPromise=null;transaction;elements;async initialize(){return this.initPromise||(this.initPromise=(async()=>{try{const e=await async function(e){const o=t("pt"),r=`${e.baseUrl??n[e.environment??"production"]}/merchants/${e.merchantId}/public-settings`;try{const e=await fetch(r);if(!e.ok){const r=String(e.status);throw new Error(`${o.errors.failedToFetchConfig}: ${r} ${e.statusText}`)}const t=await e.json();if(!t.merchant_id||!t.team_id||!t.app_id)throw new Error(o.errors.invalidConfig);return{merchantId:t.merchant_id,teamId:t.team_id,appId:t.app_id,availableMethods:t.available_methods,name:t.name}}catch(a){if(a instanceof Error)throw new Error(`${o.errors.configFetchFailed}: ${a.message}`);throw new Error(`${o.errors.configFetchFailed}: ${o.errors.unknownError}`)}}({merchantId:this.options.merchantId,environment:this.options.environment??"production",baseUrl:this.options.rinneUrl});this.config=e,this.evervault=await async function(e,r,t){return new(await o())(e,r,t)}(this.config.teamId,this.config.appId)}catch(e){throw this.initPromise=null,e}})()),this.initPromise}createElementsNamespace(){return{applePay:async(e,o)=>{if(await this.initialize(),!this.evervault||!this.config){const e=t(o.button?.locale??"pt");throw new Error(e.errors.failedToInitialize)}return u(this.evervault,this.config)(e,o)},googlePay:async(e,o)=>{if(await this.initialize(),!this.evervault||!this.config){const e=t(o.button?.locale??"pt");throw new Error(e.errors.failedToInitialize)}return p(this.evervault,this.config)(e,o)}}}createTransactionNamespace(){return{create:async e=>{if(await this.initialize(),!this.evervault||!this.config?.merchantId){const e=t("pt");throw new Error(e.errors.failedToInitialize)}return this.evervault.transactions.create({type:"payment",...e,merchantId:this.config.merchantId})}}}}export{h as Rinne};
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rinnebr/js",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "Modern payment elements library for Apple Pay, Google Pay, and other payment methods",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"packageManager": "yarn@4.9.1",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"default": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"dev": "vite",
|
|
27
|
+
"build": "yarn check && vite build",
|
|
28
|
+
"check": "yarn type-check && yarn lint && yarn format:check && yarn test",
|
|
29
|
+
"type-check": "tsc --noEmit",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"test:watch": "vitest",
|
|
32
|
+
"test:ui": "vitest --ui",
|
|
33
|
+
"lint": "eslint .",
|
|
34
|
+
"lint:fix": "eslint . --fix",
|
|
35
|
+
"format": "prettier --write .",
|
|
36
|
+
"format:check": "prettier --check .",
|
|
37
|
+
"prepare": "husky",
|
|
38
|
+
"prepublishOnly": "yarn build"
|
|
39
|
+
},
|
|
40
|
+
"keywords": [
|
|
41
|
+
"payment",
|
|
42
|
+
"apple-pay",
|
|
43
|
+
"google-pay",
|
|
44
|
+
"payment-elements",
|
|
45
|
+
"payment-buttons"
|
|
46
|
+
],
|
|
47
|
+
"author": "Rinne",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "https://github.com/rinnebr/rinne-js.git"
|
|
52
|
+
},
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/rinnebr/rinne-js/issues"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://github.com/rinnebr/rinne-js#readme",
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@eslint/js": "^9.39.1",
|
|
59
|
+
"@types/jsdom": "^27",
|
|
60
|
+
"@types/node": "^24.10.1",
|
|
61
|
+
"@vitest/ui": "^4.0.8",
|
|
62
|
+
"eslint": "^9",
|
|
63
|
+
"eslint-config-prettier": "^10.1.8",
|
|
64
|
+
"husky": "^9.1.7",
|
|
65
|
+
"jsdom": "^27.2.0",
|
|
66
|
+
"lint-staged": "^16.2.6",
|
|
67
|
+
"prettier": "^3.6.2",
|
|
68
|
+
"terser": "^5.44.1",
|
|
69
|
+
"typescript": "^5.9.3",
|
|
70
|
+
"typescript-eslint": "^8.46.4",
|
|
71
|
+
"vite": "^7.2.2",
|
|
72
|
+
"vite-plugin-dts": "^4.5.4",
|
|
73
|
+
"vitest": "^4.0.8"
|
|
74
|
+
},
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"@evervault/js": "^2.6.0"
|
|
77
|
+
}
|
|
78
|
+
}
|