zai_payment 2.0.2 → 2.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.
- checksums.yaml +4 -4
- data/badges/coverage.json +1 -1
- data/changelog.md +28 -0
- data/docs/readme.md +55 -25
- data/docs/token_auths.md +523 -0
- data/docs/user_id_field.md +1 -1
- data/docs/users.md +3 -3
- data/examples/token_auths.md +687 -0
- data/lib/zai_payment/resources/token_auth.rb +75 -0
- data/lib/zai_payment/version.rb +1 -1
- data/lib/zai_payment.rb +6 -0
- data/readme.md +31 -0
- data/token_auth_implementation_summary.md +249 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 875d506340e0ebde15266915492626d10e454df4630a74fa17881cbc04b9e6fe
|
|
4
|
+
data.tar.gz: d0fae97dd2dc5d219f46af7ea61a04e0fcc74239b0860f3cfed251782c34086a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aa334f191958e38c6e87846daac9fd33391a3e34ae0ebcca24395c501aac4b283c44fb059d6f24b70c5bd1f91f9a241abe668f81e66facfc194dfd10f66065e4
|
|
7
|
+
data.tar.gz: c81b7ceb54b8947859c62a49c98efba981e6ee0a8550e7c4bb810c23bd66aad06eece483db51d9d6cc9e1c0f05afb0e37fb1bacdadbb222fbd8dc59887e907c3
|
data/badges/coverage.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"schemaVersion": 1, "label": "coverage", "message": "95.
|
|
1
|
+
{"schemaVersion": 1, "label": "coverage", "message": "95.22%", "color": "brightgreen"}
|
data/changelog.md
CHANGED
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
## [Released]
|
|
2
|
+
## [2.1.0] - 2025-10-24
|
|
3
|
+
### Added
|
|
4
|
+
- **Token Auth API**: Token generation for bank and card accounts 🔐
|
|
5
|
+
- `ZaiPayment.token_auths.generate(user_id:, token_type:)` - Generate tokens for secure payment data collection
|
|
6
|
+
- Support for bank tokens (collecting bank account information)
|
|
7
|
+
- Support for card tokens (collecting credit card information)
|
|
8
|
+
- Token type validation (bank or card)
|
|
9
|
+
- User ID validation
|
|
10
|
+
- Full RSpec test suite for TokenAuth resource
|
|
11
|
+
- Comprehensive examples documentation in `examples/token_auths.md`
|
|
12
|
+
|
|
13
|
+
### Documentation
|
|
14
|
+
- Added detailed TokenAuth API examples with complete integration patterns
|
|
15
|
+
- Frontend integration examples (PromisePay.js)
|
|
16
|
+
- Rails controller integration examples
|
|
17
|
+
- Service object pattern examples
|
|
18
|
+
- Error handling and retry logic examples
|
|
19
|
+
- Security best practices (token expiry management, audit logging, rate limiting)
|
|
20
|
+
- Complete payment flow example
|
|
21
|
+
|
|
22
|
+
### Testing
|
|
23
|
+
- 20+ new test cases for TokenAuth resource
|
|
24
|
+
- Validation error handling tested
|
|
25
|
+
- Case-insensitive token type support tested
|
|
26
|
+
- Default parameter behavior tested
|
|
27
|
+
|
|
28
|
+
**Full Changelog**: https://github.com/Sentia/zai-payment/compare/v2.0.2...v2.1.0
|
|
29
|
+
|
|
2
30
|
## [2.0.2] - 2025-10-24
|
|
3
31
|
### Fixed
|
|
4
32
|
- **Items API**: Fixed endpoint configuration to use `core_base` instead of `va_base`
|
data/docs/readme.md
CHANGED
|
@@ -5,24 +5,30 @@ Welcome to the Zai Payment Ruby gem documentation. This guide will help you find
|
|
|
5
5
|
## 📖 Getting Started
|
|
6
6
|
|
|
7
7
|
**New to the gem?** Start here:
|
|
8
|
-
1. [Main README](../
|
|
9
|
-
2. [Authentication Guide](
|
|
10
|
-
3. [Webhook Quick Start](
|
|
8
|
+
1. [Main README](../readme.md) - Installation and basic configuration
|
|
9
|
+
2. [Authentication Guide](authentication.md) - Get tokens with two approaches (short & long way)
|
|
10
|
+
3. [Webhook Quick Start](webhook_security_quickstart.md) - Set up secure webhooks in 5 minutes
|
|
11
11
|
4. [Webhook Examples](../examples/webhooks.md) - Complete usage examples
|
|
12
12
|
|
|
13
13
|
## 🏗️ Architecture & Design
|
|
14
14
|
|
|
15
|
-
- [**
|
|
16
|
-
- [**
|
|
17
|
-
- [**
|
|
15
|
+
- [**architecture.md**](architecture.md) - System architecture and design principles
|
|
16
|
+
- [**authentication.md**](authentication.md) - OAuth2 implementation, token management, two approaches
|
|
17
|
+
- [**users.md**](users.md) - User management for payin (buyers) and payout (sellers/merchants)
|
|
18
|
+
- [**items.md**](items.md) - Item management for transactions and payments
|
|
19
|
+
- [**token_auths.md**](token_auths.md) - Token generation for secure bank and card data collection
|
|
20
|
+
- [**webhooks.md**](webhooks.md) - Webhook implementation details, best practices, and patterns
|
|
18
21
|
|
|
19
22
|
## 🔐 Security Guides
|
|
20
23
|
|
|
21
|
-
- [**
|
|
22
|
-
- [**
|
|
24
|
+
- [**webhook_security_quickstart.md**](webhook_security_quickstart.md) - Quick 5-minute security setup guide
|
|
25
|
+
- [**webhook_signature.md**](webhook_signature.md) - Detailed signature verification implementation
|
|
23
26
|
|
|
24
27
|
## 📝 Examples
|
|
25
28
|
|
|
29
|
+
- [**User Examples**](../examples/users.md) - User management examples and patterns
|
|
30
|
+
- [**Item Examples**](../examples/items.md) - Transaction and payment workflows
|
|
31
|
+
- [**Token Auth Examples**](../examples/token_auths.md) - Token generation and PromisePay.js integration
|
|
26
32
|
- [**Webhook Examples**](../examples/webhooks.md) - Comprehensive webhook usage examples including:
|
|
27
33
|
- Basic CRUD operations
|
|
28
34
|
- Rails controller implementation
|
|
@@ -34,15 +40,30 @@ Welcome to the Zai Payment Ruby gem documentation. This guide will help you find
|
|
|
34
40
|
## 🔗 Quick Links
|
|
35
41
|
|
|
36
42
|
### Authentication
|
|
37
|
-
- **Getting Started**: [Authentication Guide](
|
|
43
|
+
- **Getting Started**: [Authentication Guide](authentication.md)
|
|
38
44
|
- **Short Way**: `ZaiPayment.token` (one-liner)
|
|
39
45
|
- **Long Way**: `TokenProvider.new(config: config).bearer_token` (full control)
|
|
40
46
|
|
|
47
|
+
### Users
|
|
48
|
+
- **Guide**: [User Management](users.md)
|
|
49
|
+
- **Examples**: [User Examples](../examples/users.md)
|
|
50
|
+
- **API Reference**: [Zai Users API](https://developer.hellozai.com/reference/getallusers)
|
|
51
|
+
|
|
52
|
+
### Items
|
|
53
|
+
- **Guide**: [Item Management](items.md)
|
|
54
|
+
- **Examples**: [Item Examples](../examples/items.md)
|
|
55
|
+
- **API Reference**: [Zai Items API](https://developer.hellozai.com/reference/listitems)
|
|
56
|
+
|
|
57
|
+
### Token Auth
|
|
58
|
+
- **Guide**: [Token Auth](token_auths.md)
|
|
59
|
+
- **Examples**: [Token Auth Examples](../examples/token_auths.md)
|
|
60
|
+
- **API Reference**: [Zai Generate Token API](https://developer.hellozai.com/reference/generatetoken)
|
|
61
|
+
|
|
41
62
|
### Webhooks
|
|
42
|
-
- **Setup**: [Quick Start Guide](
|
|
63
|
+
- **Setup**: [Quick Start Guide](webhook_security_quickstart.md)
|
|
43
64
|
- **Examples**: [Complete Examples](../examples/webhooks.md)
|
|
44
|
-
- **Details**: [Technical Documentation](
|
|
45
|
-
- **Security**: [Signature Verification](
|
|
65
|
+
- **Details**: [Technical Documentation](webhooks.md)
|
|
66
|
+
- **Security**: [Signature Verification](webhook_signature.md)
|
|
46
67
|
|
|
47
68
|
### External Resources
|
|
48
69
|
- [Zai Developer Portal](https://developer.hellozai.com/)
|
|
@@ -53,29 +74,38 @@ Welcome to the Zai Payment Ruby gem documentation. This guide will help you find
|
|
|
53
74
|
|
|
54
75
|
```
|
|
55
76
|
docs/
|
|
56
|
-
├──
|
|
57
|
-
├──
|
|
58
|
-
├──
|
|
59
|
-
├──
|
|
60
|
-
├──
|
|
61
|
-
|
|
77
|
+
├── readme.md # This file - documentation index
|
|
78
|
+
├── authentication.md # OAuth2 authentication guide
|
|
79
|
+
├── users.md # User management guide
|
|
80
|
+
├── items.md # Item management guide
|
|
81
|
+
├── token_auths.md # Token generation guide (NEW!)
|
|
82
|
+
├── architecture.md # System architecture
|
|
83
|
+
├── webhooks.md # Webhook technical docs
|
|
84
|
+
├── webhook_security_quickstart.md # Quick security setup
|
|
85
|
+
└── webhook_signature.md # Signature implementation
|
|
62
86
|
|
|
63
87
|
examples/
|
|
64
|
-
|
|
88
|
+
├── users.md # User management examples
|
|
89
|
+
├── items.md # Item examples
|
|
90
|
+
├── token_auths.md # Token auth examples (NEW!)
|
|
91
|
+
└── webhooks.md # Webhook examples
|
|
65
92
|
```
|
|
66
93
|
|
|
67
94
|
## 💡 Tips
|
|
68
95
|
|
|
69
|
-
- **Getting tokens?** Check [
|
|
70
|
-
- **
|
|
71
|
-
- **
|
|
72
|
-
- **
|
|
73
|
-
- **
|
|
96
|
+
- **Getting tokens?** Check [authentication.md](authentication.md) for both approaches
|
|
97
|
+
- **Managing users?** See [users.md](users.md) for payin and payout user guides
|
|
98
|
+
- **Creating transactions?** Review [items.md](items.md) for item management
|
|
99
|
+
- **Collecting payment data?** See [token_auths.md](token_auths.md) for secure token generation
|
|
100
|
+
- **Looking for code examples?** Check the [examples](../examples/) directory
|
|
101
|
+
- **Need quick setup?** See [webhook_security_quickstart.md](webhook_security_quickstart.md)
|
|
102
|
+
- **Want to understand the design?** Read [architecture.md](architecture.md)
|
|
103
|
+
- **Security details?** Review [webhook_signature.md](webhook_signature.md)
|
|
74
104
|
|
|
75
105
|
## 🆘 Need Help?
|
|
76
106
|
|
|
77
107
|
1. Check the relevant documentation section above
|
|
78
|
-
2. Review the [examples](../examples/
|
|
108
|
+
2. Review the [examples](../examples/)
|
|
79
109
|
3. Consult the [Zai API documentation](https://developer.hellozai.com/)
|
|
80
110
|
4. Open an issue on GitHub
|
|
81
111
|
|
data/docs/token_auths.md
ADDED
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
# Token Auth API
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The TokenAuth resource provides secure token generation for collecting bank account and credit card information. These tokens are used with the PromisePay.js library to securely transmit sensitive payment data without it ever touching your server.
|
|
6
|
+
|
|
7
|
+
This is particularly useful for PCI compliance when collecting credit card information, and for securely collecting bank account details.
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [When to Use Token Auth](#when-to-use-token-auth)
|
|
12
|
+
- [How It Works](#how-it-works)
|
|
13
|
+
- [API Methods](#api-methods)
|
|
14
|
+
- [Token Types](#token-types)
|
|
15
|
+
- [Security Considerations](#security-considerations)
|
|
16
|
+
- [Integration Guide](#integration-guide)
|
|
17
|
+
- [Error Handling](#error-handling)
|
|
18
|
+
- [Best Practices](#best-practices)
|
|
19
|
+
|
|
20
|
+
## When to Use Token Auth
|
|
21
|
+
|
|
22
|
+
You should use Token Auth when you need to:
|
|
23
|
+
|
|
24
|
+
1. **Collect Credit Card Information** - Generate a card token for buyers to securely enter their credit card details
|
|
25
|
+
2. **Collect Bank Account Details** - Generate a bank token for sellers to securely provide their bank account information
|
|
26
|
+
3. **PCI Compliance** - Ensure sensitive payment data never touches your server
|
|
27
|
+
4. **Frontend Integration** - Use with PromisePay.js to handle payment data collection on the client side
|
|
28
|
+
|
|
29
|
+
## How It Works
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
1. Backend (Your Server)
|
|
33
|
+
↓
|
|
34
|
+
Generate token using ZaiPayment.token_auths.generate()
|
|
35
|
+
↓
|
|
36
|
+
Return token to frontend
|
|
37
|
+
|
|
38
|
+
2. Frontend (Browser/App)
|
|
39
|
+
↓
|
|
40
|
+
Use PromisePay.js with the token
|
|
41
|
+
↓
|
|
42
|
+
User enters payment details
|
|
43
|
+
↓
|
|
44
|
+
PromisePay.js sends data directly to Zai (not your server)
|
|
45
|
+
↓
|
|
46
|
+
Returns payment account ID
|
|
47
|
+
|
|
48
|
+
3. Backend (Your Server)
|
|
49
|
+
↓
|
|
50
|
+
Use payment account ID to create items/transactions
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## API Methods
|
|
54
|
+
|
|
55
|
+
### `generate(user_id:, token_type:)`
|
|
56
|
+
|
|
57
|
+
Generate a token for bank or card account data collection.
|
|
58
|
+
|
|
59
|
+
**Parameters:**
|
|
60
|
+
- `user_id` (String, required) - The ID of the buyer or seller user (already created)
|
|
61
|
+
- `token_type` (String, optional) - Type of token to generate: `'bank'` or `'card'` (default: `'bank'`)
|
|
62
|
+
|
|
63
|
+
**Returns:**
|
|
64
|
+
- `Response` object containing the generated token and metadata
|
|
65
|
+
|
|
66
|
+
**Example:**
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
# Generate a bank token
|
|
70
|
+
response = ZaiPayment.token_auths.generate(
|
|
71
|
+
user_id: "seller-68611249",
|
|
72
|
+
token_type: "bank"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
token = response.data['token_auth']['token']
|
|
76
|
+
# => "tok_bank_abc123xyz..."
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Token Types
|
|
80
|
+
|
|
81
|
+
### Bank Tokens
|
|
82
|
+
|
|
83
|
+
Bank tokens are used to collect bank account information from payout users (sellers/merchants).
|
|
84
|
+
|
|
85
|
+
**Use Case:** Collecting bank account details for disbursements to sellers.
|
|
86
|
+
|
|
87
|
+
**Typical Flow:**
|
|
88
|
+
1. Create a payout user (seller)
|
|
89
|
+
2. Generate a bank token for the seller
|
|
90
|
+
3. Send token to frontend
|
|
91
|
+
4. Use PromisePay.js to collect bank account details
|
|
92
|
+
5. Receive bank account ID from Zai
|
|
93
|
+
6. Associate bank account with items/transactions
|
|
94
|
+
|
|
95
|
+
**Example:**
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
# Step 1: Create seller
|
|
99
|
+
seller = ZaiPayment.users.create(
|
|
100
|
+
user_type: "payout",
|
|
101
|
+
email: "seller@example.com",
|
|
102
|
+
first_name: "Jane",
|
|
103
|
+
last_name: "Smith",
|
|
104
|
+
country: "AUS",
|
|
105
|
+
dob: "01/01/1990",
|
|
106
|
+
address_line1: "456 Market St",
|
|
107
|
+
city: "Sydney",
|
|
108
|
+
state: "NSW",
|
|
109
|
+
zip: "2000"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
seller_id = seller.data['users']['id']
|
|
113
|
+
|
|
114
|
+
# Step 2: Generate bank token
|
|
115
|
+
token_response = ZaiPayment.token_auths.generate(
|
|
116
|
+
user_id: seller_id,
|
|
117
|
+
token_type: "bank"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
bank_token = token_response.data['token_auth']['token']
|
|
121
|
+
|
|
122
|
+
# Step 3: Send to frontend for PromisePay.js integration
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Card Tokens
|
|
126
|
+
|
|
127
|
+
Card tokens are used to collect credit card information from payin users (buyers).
|
|
128
|
+
|
|
129
|
+
**Use Case:** Collecting credit card details for payments from buyers.
|
|
130
|
+
|
|
131
|
+
**Typical Flow:**
|
|
132
|
+
1. Create a payin user (buyer)
|
|
133
|
+
2. Generate a card token for the buyer
|
|
134
|
+
3. Send token to frontend
|
|
135
|
+
4. Use PromisePay.js to collect credit card details
|
|
136
|
+
5. Receive card account ID from Zai
|
|
137
|
+
6. Use card account to process payments
|
|
138
|
+
|
|
139
|
+
**Example:**
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
# Step 1: Create buyer
|
|
143
|
+
buyer = ZaiPayment.users.create(
|
|
144
|
+
user_type: "payin",
|
|
145
|
+
email: "buyer@example.com",
|
|
146
|
+
first_name: "John",
|
|
147
|
+
last_name: "Doe",
|
|
148
|
+
country: "USA"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
buyer_id = buyer.data['users']['id']
|
|
152
|
+
|
|
153
|
+
# Step 2: Generate card token
|
|
154
|
+
token_response = ZaiPayment.token_auths.generate(
|
|
155
|
+
user_id: buyer_id,
|
|
156
|
+
token_type: "card"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
card_token = token_response.data['token_auth']['token']
|
|
160
|
+
|
|
161
|
+
# Step 3: Send to frontend for PromisePay.js integration
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Security Considerations
|
|
165
|
+
|
|
166
|
+
### Token Expiration
|
|
167
|
+
|
|
168
|
+
Tokens have a limited lifespan (typically 1 hour). Always:
|
|
169
|
+
- Check the `expires_at` field in the response
|
|
170
|
+
- Generate fresh tokens for each payment session
|
|
171
|
+
- Don't store tokens for later use
|
|
172
|
+
- Handle token expiration gracefully on the frontend
|
|
173
|
+
|
|
174
|
+
### Token Scope
|
|
175
|
+
|
|
176
|
+
Each token is:
|
|
177
|
+
- **User-specific**: Tied to a single user ID
|
|
178
|
+
- **Single-use**: Should be used once per payment session
|
|
179
|
+
- **Type-specific**: Bank tokens only for bank accounts, card tokens only for cards
|
|
180
|
+
|
|
181
|
+
### PCI Compliance
|
|
182
|
+
|
|
183
|
+
Token Auth helps maintain PCI compliance by:
|
|
184
|
+
- Preventing card data from touching your server
|
|
185
|
+
- Using secure HTTPS transmission
|
|
186
|
+
- Leveraging Zai's PCI-compliant infrastructure
|
|
187
|
+
- Minimizing your PCI scope
|
|
188
|
+
|
|
189
|
+
## Integration Guide
|
|
190
|
+
|
|
191
|
+
### Backend Integration
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
# app/controllers/payment_tokens_controller.rb
|
|
195
|
+
class PaymentTokensController < ApplicationController
|
|
196
|
+
before_action :authenticate_user!
|
|
197
|
+
|
|
198
|
+
def create
|
|
199
|
+
user_id = current_user.zai_user_id
|
|
200
|
+
token_type = params[:token_type] || 'card'
|
|
201
|
+
|
|
202
|
+
response = ZaiPayment.token_auths.generate(
|
|
203
|
+
user_id: user_id,
|
|
204
|
+
token_type: token_type
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
render json: {
|
|
208
|
+
token: response.data['token_auth']['token'],
|
|
209
|
+
expires_at: response.data['token_auth']['expires_at']
|
|
210
|
+
}
|
|
211
|
+
rescue ZaiPayment::Errors::ApiError => e
|
|
212
|
+
render json: { error: e.message }, status: :bad_gateway
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Frontend Integration (JavaScript)
|
|
218
|
+
|
|
219
|
+
```html
|
|
220
|
+
<!-- Include PromisePay.js -->
|
|
221
|
+
<script src="https://cdn.assemblypay.com/promisepay.js"></script>
|
|
222
|
+
|
|
223
|
+
<script>
|
|
224
|
+
// 1. Fetch token from your backend
|
|
225
|
+
async function getPaymentToken(tokenType) {
|
|
226
|
+
const response = await fetch('/api/payment_tokens', {
|
|
227
|
+
method: 'POST',
|
|
228
|
+
headers: { 'Content-Type': 'application/json' },
|
|
229
|
+
body: JSON.stringify({ token_type: tokenType })
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const data = await response.json();
|
|
233
|
+
return data.token;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// 2. Use token with PromisePay.js to collect card details
|
|
237
|
+
async function collectCardDetails() {
|
|
238
|
+
const token = await getPaymentToken('card');
|
|
239
|
+
|
|
240
|
+
PromisePay.setToken(token);
|
|
241
|
+
|
|
242
|
+
PromisePay.createCardAccount({
|
|
243
|
+
card_number: document.getElementById('card_number').value,
|
|
244
|
+
expiry_month: document.getElementById('expiry_month').value,
|
|
245
|
+
expiry_year: document.getElementById('expiry_year').value,
|
|
246
|
+
cvv: document.getElementById('cvv').value
|
|
247
|
+
}, function(response) {
|
|
248
|
+
if (response.error) {
|
|
249
|
+
console.error('Error:', response.error);
|
|
250
|
+
} else {
|
|
251
|
+
// Card account created successfully
|
|
252
|
+
const cardAccountId = response.card_accounts.id;
|
|
253
|
+
// Send cardAccountId to your backend to create item/transaction
|
|
254
|
+
processPayment(cardAccountId);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 3. Use token with PromisePay.js to collect bank details
|
|
260
|
+
async function collectBankDetails() {
|
|
261
|
+
const token = await getPaymentToken('bank');
|
|
262
|
+
|
|
263
|
+
PromisePay.setToken(token);
|
|
264
|
+
|
|
265
|
+
PromisePay.createBankAccount({
|
|
266
|
+
account_name: document.getElementById('account_name').value,
|
|
267
|
+
account_number: document.getElementById('account_number').value,
|
|
268
|
+
routing_number: document.getElementById('routing_number').value,
|
|
269
|
+
account_type: 'savings' // or 'checking'
|
|
270
|
+
}, function(response) {
|
|
271
|
+
if (response.error) {
|
|
272
|
+
console.error('Error:', response.error);
|
|
273
|
+
} else {
|
|
274
|
+
// Bank account created successfully
|
|
275
|
+
const bankAccountId = response.bank_accounts.id;
|
|
276
|
+
// Send bankAccountId to your backend
|
|
277
|
+
saveBankAccount(bankAccountId);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
</script>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Complete Payment Flow
|
|
285
|
+
|
|
286
|
+
```ruby
|
|
287
|
+
# 1. Create users (buyer and seller)
|
|
288
|
+
buyer = ZaiPayment.users.create(
|
|
289
|
+
user_type: "payin",
|
|
290
|
+
email: "buyer@example.com",
|
|
291
|
+
first_name: "John",
|
|
292
|
+
last_name: "Doe",
|
|
293
|
+
country: "USA"
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
seller = ZaiPayment.users.create(
|
|
297
|
+
user_type: "payout",
|
|
298
|
+
email: "seller@example.com",
|
|
299
|
+
first_name: "Jane",
|
|
300
|
+
last_name: "Smith",
|
|
301
|
+
country: "AUS",
|
|
302
|
+
dob: "01/01/1990",
|
|
303
|
+
address_line1: "456 Market St",
|
|
304
|
+
city: "Sydney",
|
|
305
|
+
state: "NSW",
|
|
306
|
+
zip: "2000"
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
# 2. Generate card token for buyer
|
|
310
|
+
card_token_response = ZaiPayment.token_auths.generate(
|
|
311
|
+
user_id: buyer.data['users']['id'],
|
|
312
|
+
token_type: "card"
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
# Send card_token to frontend, collect card details, get card_account_id
|
|
316
|
+
|
|
317
|
+
# 3. Generate bank token for seller
|
|
318
|
+
bank_token_response = ZaiPayment.token_auths.generate(
|
|
319
|
+
user_id: seller.data['users']['id'],
|
|
320
|
+
token_type: "bank"
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# Send bank_token to frontend, collect bank details, get bank_account_id
|
|
324
|
+
|
|
325
|
+
# 4. Create item for payment
|
|
326
|
+
item = ZaiPayment.items.create(
|
|
327
|
+
name: "Product Purchase",
|
|
328
|
+
amount: 10000, # $100.00
|
|
329
|
+
payment_type: 2, # Credit card
|
|
330
|
+
buyer_id: buyer.data['users']['id'],
|
|
331
|
+
seller_id: seller.data['users']['id']
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
# 5. Process payment using card_account_id
|
|
335
|
+
# (Use Items Actions API or Payment API - to be implemented)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Error Handling
|
|
339
|
+
|
|
340
|
+
### Common Errors
|
|
341
|
+
|
|
342
|
+
```ruby
|
|
343
|
+
begin
|
|
344
|
+
response = ZaiPayment.token_auths.generate(
|
|
345
|
+
user_id: user_id,
|
|
346
|
+
token_type: token_type
|
|
347
|
+
)
|
|
348
|
+
rescue ZaiPayment::Errors::ValidationError => e
|
|
349
|
+
# Invalid user_id or token_type
|
|
350
|
+
{ error: "Validation error: #{e.message}", retryable: false }
|
|
351
|
+
rescue ZaiPayment::Errors::NotFoundError => e
|
|
352
|
+
# User not found
|
|
353
|
+
{ error: "User not found: #{user_id}", retryable: false }
|
|
354
|
+
rescue ZaiPayment::Errors::UnauthorizedError => e
|
|
355
|
+
# Authentication failed
|
|
356
|
+
{ error: "Authentication failed", retryable: false }
|
|
357
|
+
rescue ZaiPayment::Errors::RateLimitError => e
|
|
358
|
+
# Rate limit exceeded
|
|
359
|
+
{ error: "Rate limit exceeded", retryable: true, retry_after: 60 }
|
|
360
|
+
rescue ZaiPayment::Errors::ServerError => e
|
|
361
|
+
# Server error (5xx)
|
|
362
|
+
{ error: "Server error", retryable: true }
|
|
363
|
+
rescue ZaiPayment::Errors::TimeoutError => e
|
|
364
|
+
# Request timeout
|
|
365
|
+
{ error: "Timeout", retryable: true }
|
|
366
|
+
end
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Validation Errors
|
|
370
|
+
|
|
371
|
+
| Error | Cause | Solution |
|
|
372
|
+
|-------|-------|----------|
|
|
373
|
+
| `user_id is required and cannot be blank` | Missing or empty user_id | Provide a valid user ID |
|
|
374
|
+
| `token_type must be one of: bank, card` | Invalid token_type | Use 'bank' or 'card' |
|
|
375
|
+
| `User not found` | User doesn't exist | Create user first |
|
|
376
|
+
|
|
377
|
+
## Best Practices
|
|
378
|
+
|
|
379
|
+
### 1. Token Lifecycle Management
|
|
380
|
+
|
|
381
|
+
```ruby
|
|
382
|
+
class TokenManager
|
|
383
|
+
def initialize(user_id)
|
|
384
|
+
@user_id = user_id
|
|
385
|
+
@cache = {}
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def get_token(type)
|
|
389
|
+
cache_key = "#{@user_id}_#{type}"
|
|
390
|
+
|
|
391
|
+
# Don't reuse expired tokens
|
|
392
|
+
if @cache[cache_key] && !token_expired?(@cache[cache_key][:expires_at])
|
|
393
|
+
return @cache[cache_key][:token]
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# Generate fresh token
|
|
397
|
+
response = ZaiPayment.token_auths.generate(
|
|
398
|
+
user_id: @user_id,
|
|
399
|
+
token_type: type
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
token_data = response.data['token_auth']
|
|
403
|
+
|
|
404
|
+
@cache[cache_key] = {
|
|
405
|
+
token: token_data['token'],
|
|
406
|
+
expires_at: Time.parse(token_data['expires_at'])
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
token_data['token']
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
private
|
|
413
|
+
|
|
414
|
+
def token_expired?(expires_at)
|
|
415
|
+
# Consider expired 5 minutes before actual expiry
|
|
416
|
+
expires_at < Time.now + 300
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### 2. Audit Logging
|
|
422
|
+
|
|
423
|
+
```ruby
|
|
424
|
+
def generate_token_with_audit(user_id:, token_type:, context: {})
|
|
425
|
+
start_time = Time.now
|
|
426
|
+
|
|
427
|
+
begin
|
|
428
|
+
response = ZaiPayment.token_auths.generate(
|
|
429
|
+
user_id: user_id,
|
|
430
|
+
token_type: token_type
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
log_event(
|
|
434
|
+
event: 'token_generated',
|
|
435
|
+
user_id: user_id,
|
|
436
|
+
token_type: token_type,
|
|
437
|
+
success: true,
|
|
438
|
+
duration: Time.now - start_time,
|
|
439
|
+
context: context
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
response
|
|
443
|
+
rescue => e
|
|
444
|
+
log_event(
|
|
445
|
+
event: 'token_generation_failed',
|
|
446
|
+
user_id: user_id,
|
|
447
|
+
token_type: token_type,
|
|
448
|
+
success: false,
|
|
449
|
+
error: e.message,
|
|
450
|
+
duration: Time.now - start_time,
|
|
451
|
+
context: context
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
raise
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### 3. Rate Limiting
|
|
460
|
+
|
|
461
|
+
```ruby
|
|
462
|
+
class RateLimitedTokenGenerator
|
|
463
|
+
def initialize(user_id)
|
|
464
|
+
@user_id = user_id
|
|
465
|
+
@redis = Redis.new
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def generate(token_type:, limit: 10, window: 3600)
|
|
469
|
+
rate_limit_key = "token_gen:#{@user_id}:#{Time.now.to_i / window}"
|
|
470
|
+
current_count = @redis.incr(rate_limit_key)
|
|
471
|
+
@redis.expire(rate_limit_key, window)
|
|
472
|
+
|
|
473
|
+
if current_count > limit
|
|
474
|
+
raise "Rate limit exceeded: #{limit} tokens per #{window} seconds"
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
ZaiPayment.token_auths.generate(
|
|
478
|
+
user_id: @user_id,
|
|
479
|
+
token_type: token_type
|
|
480
|
+
)
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### 4. Retry Logic
|
|
486
|
+
|
|
487
|
+
```ruby
|
|
488
|
+
def generate_token_with_retry(user_id, token_type, max_retries: 3)
|
|
489
|
+
retries = 0
|
|
490
|
+
|
|
491
|
+
begin
|
|
492
|
+
ZaiPayment.token_auths.generate(
|
|
493
|
+
user_id: user_id,
|
|
494
|
+
token_type: token_type
|
|
495
|
+
)
|
|
496
|
+
rescue ZaiPayment::Errors::RateLimitError,
|
|
497
|
+
ZaiPayment::Errors::ServerError,
|
|
498
|
+
ZaiPayment::Errors::TimeoutError => e
|
|
499
|
+
retries += 1
|
|
500
|
+
|
|
501
|
+
if retries < max_retries
|
|
502
|
+
sleep_time = 2 ** retries # Exponential backoff
|
|
503
|
+
sleep(sleep_time)
|
|
504
|
+
retry
|
|
505
|
+
else
|
|
506
|
+
raise
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
## API Reference
|
|
513
|
+
|
|
514
|
+
For complete API documentation, see:
|
|
515
|
+
- [Zai: Generate Token API Reference](https://developer.hellozai.com/reference/generatetoken)
|
|
516
|
+
- [Token Auth Examples](../examples/token_auths.md)
|
|
517
|
+
|
|
518
|
+
## Related Resources
|
|
519
|
+
|
|
520
|
+
- [User Management](users.md) - Create users before generating tokens
|
|
521
|
+
- [Item Management](items.md) - Use payment accounts to create items
|
|
522
|
+
- [PromisePay.js Documentation](https://developer.hellozai.com/docs/promisepay-js)
|
|
523
|
+
|