zai_payment 2.0.2 → 2.2.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.
@@ -0,0 +1,687 @@
1
+ # Token Auth Examples
2
+
3
+ This file contains practical examples of using the TokenAuth resource to generate tokens for bank or card accounts.
4
+
5
+ ## Table of Contents
6
+ - [Basic Usage](#basic-usage)
7
+ - [Bank Tokens](#bank-tokens)
8
+ - [Card Tokens](#card-tokens)
9
+ - [Integration Examples](#integration-examples)
10
+ - [Error Handling](#error-handling)
11
+ - [Security Best Practices](#security-best-practices)
12
+
13
+ ## Basic Usage
14
+
15
+ ### Generate a Bank Token (Default)
16
+
17
+ ```ruby
18
+ # Generate a bank token (default token_type)
19
+ response = ZaiPayment.token_auths.generate(
20
+ user_id: "seller-68611249"
21
+ )
22
+
23
+ puts response.data
24
+ # => {
25
+ # "token_auth" => {
26
+ # "token" => "tok_bank_abc123...",
27
+ # "user_id" => "seller-68611249",
28
+ # "token_type" => "bank",
29
+ # "created_at" => "2025-10-24T12:00:00Z",
30
+ # "expires_at" => "2025-10-24T13:00:00Z"
31
+ # }
32
+ # }
33
+
34
+ token = response.data['token_auth']['token']
35
+ ```
36
+
37
+ ### Generate with Explicit Token Type
38
+
39
+ ```ruby
40
+ # Generate a bank token (explicit)
41
+ response = ZaiPayment.token_auths.generate(
42
+ user_id: "seller-68611249",
43
+ token_type: "bank"
44
+ )
45
+
46
+ # Generate a card token (explicit)
47
+ response = ZaiPayment.token_auths.generate(
48
+ user_id: "buyer-12345",
49
+ token_type: "card"
50
+ )
51
+ ```
52
+
53
+ ## Bank Tokens
54
+
55
+ Bank tokens are used for securely collecting bank account information from users.
56
+
57
+ ### Example: Collect Bank Account Details
58
+
59
+ ```ruby
60
+ # Step 1: Create a payout user (seller)
61
+ seller = ZaiPayment.users.create(
62
+ user_type: "payout",
63
+ email: "seller@example.com",
64
+ first_name: "Jane",
65
+ last_name: "Smith",
66
+ country: "AUS",
67
+ dob: "01/01/1990",
68
+ address_line1: "456 Market St",
69
+ city: "Sydney",
70
+ state: "NSW",
71
+ zip: "2000"
72
+ )
73
+
74
+ seller_id = seller.data['users']['id']
75
+
76
+ # Step 2: Generate a bank token for the seller
77
+ token_response = ZaiPayment.token_auths.generate(
78
+ user_id: seller_id,
79
+ token_type: "bank"
80
+ )
81
+
82
+ bank_token = token_response.data['token_auth']['token']
83
+
84
+ # Step 3: Use this token with PromisePay.js on the frontend
85
+ # to securely collect bank account details
86
+ puts "Bank Token: #{bank_token}"
87
+ puts "Send this token to your frontend to use with PromisePay.js"
88
+ ```
89
+
90
+ ### Example: Multiple Bank Accounts
91
+
92
+ ```ruby
93
+ # Generate tokens for multiple sellers to collect their bank account details
94
+ sellers = ["seller-001", "seller-002", "seller-003"]
95
+
96
+ bank_tokens = sellers.map do |seller_id|
97
+ response = ZaiPayment.token_auths.generate(
98
+ user_id: seller_id,
99
+ token_type: "bank"
100
+ )
101
+
102
+ {
103
+ seller_id: seller_id,
104
+ token: response.data['token_auth']['token'],
105
+ expires_at: response.data['token_auth']['expires_at']
106
+ }
107
+ end
108
+
109
+ puts bank_tokens
110
+ ```
111
+
112
+ ## Card Tokens
113
+
114
+ Card tokens are used for securely collecting credit card information from buyers.
115
+
116
+ ### Example: Collect Card Details for Payment
117
+
118
+ ```ruby
119
+ # Step 1: Create a payin user (buyer)
120
+ buyer = ZaiPayment.users.create(
121
+ user_type: "payin",
122
+ email: "buyer@example.com",
123
+ first_name: "John",
124
+ last_name: "Doe",
125
+ country: "USA"
126
+ )
127
+
128
+ buyer_id = buyer.data['users']['id']
129
+
130
+ # Step 2: Generate a card token for the buyer
131
+ token_response = ZaiPayment.token_auths.generate(
132
+ user_id: buyer_id,
133
+ token_type: "card"
134
+ )
135
+
136
+ card_token = token_response.data['token_auth']['token']
137
+
138
+ # Step 3: Use this token with PromisePay.js on the frontend
139
+ # to securely collect credit card details
140
+ puts "Card Token: #{card_token}"
141
+ puts "Send this token to your frontend to use with PromisePay.js"
142
+ ```
143
+
144
+ ### Example: Card Token for Checkout Flow
145
+
146
+ ```ruby
147
+ # In a checkout flow, generate a card token for the buyer
148
+ def generate_card_token_for_checkout(buyer_id)
149
+ response = ZaiPayment.token_auths.generate(
150
+ user_id: buyer_id,
151
+ token_type: "card"
152
+ )
153
+
154
+ token_data = response.data['token_auth']
155
+
156
+ {
157
+ token: token_data['token'],
158
+ expires_at: token_data['expires_at'],
159
+ # Send this to your frontend
160
+ frontend_data: {
161
+ token: token_data['token'],
162
+ user_id: buyer_id
163
+ }
164
+ }
165
+ end
166
+
167
+ # Usage
168
+ checkout_data = generate_card_token_for_checkout("buyer-12345")
169
+ puts checkout_data[:frontend_data].to_json
170
+ ```
171
+
172
+ ## Integration Examples
173
+
174
+ ### Example: Rails Controller Integration
175
+
176
+ ```ruby
177
+ # app/controllers/payment_tokens_controller.rb
178
+ class PaymentTokensController < ApplicationController
179
+ before_action :authenticate_user!
180
+
181
+ # POST /payment_tokens/bank
182
+ def create_bank_token
183
+ user_id = current_user.zai_seller_id
184
+
185
+ response = ZaiPayment.token_auths.generate(
186
+ user_id: user_id,
187
+ token_type: "bank"
188
+ )
189
+
190
+ render json: {
191
+ token: response.data['token_auth']['token'],
192
+ expires_at: response.data['token_auth']['expires_at']
193
+ }
194
+ rescue ZaiPayment::Errors::ValidationError => e
195
+ render json: { error: e.message }, status: :unprocessable_entity
196
+ rescue ZaiPayment::Errors::ApiError => e
197
+ render json: { error: "Failed to generate token" }, status: :bad_gateway
198
+ end
199
+
200
+ # POST /payment_tokens/card
201
+ def create_card_token
202
+ user_id = current_user.zai_buyer_id
203
+
204
+ response = ZaiPayment.token_auths.generate(
205
+ user_id: user_id,
206
+ token_type: "card"
207
+ )
208
+
209
+ render json: {
210
+ token: response.data['token_auth']['token'],
211
+ expires_at: response.data['token_auth']['expires_at']
212
+ }
213
+ rescue ZaiPayment::Errors::ValidationError => e
214
+ render json: { error: e.message }, status: :unprocessable_entity
215
+ rescue ZaiPayment::Errors::ApiError => e
216
+ render json: { error: "Failed to generate token" }, status: :bad_gateway
217
+ end
218
+ end
219
+ ```
220
+
221
+ ### Example: Service Object Pattern
222
+
223
+ ```ruby
224
+ # app/services/token_generator_service.rb
225
+ class TokenGeneratorService
226
+ def initialize(user_id)
227
+ @user_id = user_id
228
+ end
229
+
230
+ def generate_bank_token
231
+ generate_token(type: 'bank')
232
+ end
233
+
234
+ def generate_card_token
235
+ generate_token(type: 'card')
236
+ end
237
+
238
+ private
239
+
240
+ def generate_token(type:)
241
+ response = ZaiPayment.token_auths.generate(
242
+ user_id: @user_id,
243
+ token_type: type
244
+ )
245
+
246
+ token_data = response.data['token_auth']
247
+
248
+ {
249
+ success: true,
250
+ token: token_data['token'],
251
+ expires_at: token_data['expires_at'],
252
+ type: type
253
+ }
254
+ rescue ZaiPayment::Errors::ValidationError => e
255
+ {
256
+ success: false,
257
+ error: e.message,
258
+ type: :validation_error
259
+ }
260
+ rescue ZaiPayment::Errors::ApiError => e
261
+ {
262
+ success: false,
263
+ error: "API error: #{e.message}",
264
+ type: :api_error
265
+ }
266
+ end
267
+ end
268
+
269
+ # Usage
270
+ service = TokenGeneratorService.new("seller-123")
271
+ result = service.generate_bank_token
272
+
273
+ if result[:success]
274
+ puts "Token: #{result[:token]}"
275
+ else
276
+ puts "Error: #{result[:error]}"
277
+ end
278
+ ```
279
+
280
+ ### Example: Frontend Integration
281
+
282
+ ```ruby
283
+ # Backend: Generate and send token to frontend
284
+ def generate_frontend_token(user_id, token_type)
285
+ response = ZaiPayment.token_auths.generate(
286
+ user_id: user_id,
287
+ token_type: token_type
288
+ )
289
+
290
+ token_data = response.data['token_auth']
291
+
292
+ # Return data formatted for frontend
293
+ {
294
+ token: token_data['token'],
295
+ expiresAt: token_data['expires_at'],
296
+ userId: user_id,
297
+ tokenType: token_type
298
+ }
299
+ end
300
+
301
+ # Frontend JavaScript (example)
302
+ # <script src="https://cdn.assemblypay.com/promisepay.js"></script>
303
+ # <script>
304
+ # // Receive token from backend
305
+ # fetch('/api/payment_tokens/card', { method: 'POST' })
306
+ # .then(res => res.json())
307
+ # .then(data => {
308
+ # // Use the token with PromisePay.js
309
+ # PromisePay.setToken(data.token);
310
+ #
311
+ # // Collect card details
312
+ # PromisePay.createCardAccount({
313
+ # card_number: '4111111111111111',
314
+ # expiry_month: '12',
315
+ # expiry_year: '2025',
316
+ # cvv: '123'
317
+ # }, function(response) {
318
+ # console.log('Card account created:', response);
319
+ # });
320
+ # });
321
+ # </script>
322
+ ```
323
+
324
+ ## Error Handling
325
+
326
+ ### Example: Comprehensive Error Handling
327
+
328
+ ```ruby
329
+ def generate_token_with_error_handling(user_id, token_type)
330
+ begin
331
+ response = ZaiPayment.token_auths.generate(
332
+ user_id: user_id,
333
+ token_type: token_type
334
+ )
335
+
336
+ {
337
+ success: true,
338
+ token: response.data['token_auth']['token'],
339
+ expires_at: response.data['token_auth']['expires_at']
340
+ }
341
+ rescue ZaiPayment::Errors::ValidationError => e
342
+ # Invalid parameters (user_id or token_type)
343
+ {
344
+ success: false,
345
+ error: e.message,
346
+ error_type: :validation,
347
+ retryable: false
348
+ }
349
+ rescue ZaiPayment::Errors::UnauthorizedError => e
350
+ # Authentication failed
351
+ {
352
+ success: false,
353
+ error: "Authentication failed",
354
+ error_type: :auth,
355
+ retryable: false
356
+ }
357
+ rescue ZaiPayment::Errors::NotFoundError => e
358
+ # User not found
359
+ {
360
+ success: false,
361
+ error: "User not found: #{user_id}",
362
+ error_type: :not_found,
363
+ retryable: false
364
+ }
365
+ rescue ZaiPayment::Errors::RateLimitError => e
366
+ # Rate limit exceeded
367
+ {
368
+ success: false,
369
+ error: "Rate limit exceeded",
370
+ error_type: :rate_limit,
371
+ retryable: true,
372
+ retry_after: 60 # seconds
373
+ }
374
+ rescue ZaiPayment::Errors::ServerError => e
375
+ # Server error (5xx)
376
+ {
377
+ success: false,
378
+ error: "Server error",
379
+ error_type: :server,
380
+ retryable: true
381
+ }
382
+ rescue ZaiPayment::Errors::TimeoutError => e
383
+ # Request timeout
384
+ {
385
+ success: false,
386
+ error: "Request timeout",
387
+ error_type: :timeout,
388
+ retryable: true
389
+ }
390
+ rescue ZaiPayment::Errors::ApiError => e
391
+ # Generic API error
392
+ {
393
+ success: false,
394
+ error: e.message,
395
+ error_type: :api,
396
+ retryable: false
397
+ }
398
+ end
399
+ end
400
+
401
+ # Usage
402
+ result = generate_token_with_error_handling("buyer-123", "card")
403
+
404
+ if result[:success]
405
+ puts "Token: #{result[:token]}"
406
+ else
407
+ puts "Error: #{result[:error]}"
408
+ puts "Retryable: #{result[:retryable]}" if result[:retryable]
409
+ end
410
+ ```
411
+
412
+ ### Example: Retry Logic
413
+
414
+ ```ruby
415
+ def generate_token_with_retry(user_id, token_type, max_retries: 3)
416
+ retries = 0
417
+
418
+ begin
419
+ response = ZaiPayment.token_auths.generate(
420
+ user_id: user_id,
421
+ token_type: token_type
422
+ )
423
+
424
+ response.data['token_auth']['token']
425
+ rescue ZaiPayment::Errors::RateLimitError,
426
+ ZaiPayment::Errors::ServerError,
427
+ ZaiPayment::Errors::TimeoutError => e
428
+ retries += 1
429
+
430
+ if retries < max_retries
431
+ sleep_time = 2 ** retries # Exponential backoff: 2, 4, 8 seconds
432
+ puts "Retry #{retries}/#{max_retries} after #{sleep_time}s..."
433
+ sleep(sleep_time)
434
+ retry
435
+ else
436
+ puts "Max retries reached"
437
+ raise
438
+ end
439
+ end
440
+ end
441
+
442
+ # Usage
443
+ begin
444
+ token = generate_token_with_retry("buyer-123", "card")
445
+ puts "Token: #{token}"
446
+ rescue => e
447
+ puts "Failed after retries: #{e.message}"
448
+ end
449
+ ```
450
+
451
+ ## Security Best Practices
452
+
453
+ ### Example: Token Expiry Management
454
+
455
+ ```ruby
456
+ class TokenManager
457
+ def initialize(user_id)
458
+ @user_id = user_id
459
+ @cache = {}
460
+ end
461
+
462
+ def get_bank_token
463
+ get_token('bank')
464
+ end
465
+
466
+ def get_card_token
467
+ get_token('card')
468
+ end
469
+
470
+ private
471
+
472
+ def get_token(type)
473
+ cache_key = "#{@user_id}_#{type}"
474
+
475
+ # Check if we have a valid cached token
476
+ if @cache[cache_key] && !token_expired?(@cache[cache_key][:expires_at])
477
+ return @cache[cache_key][:token]
478
+ end
479
+
480
+ # Generate new token
481
+ response = ZaiPayment.token_auths.generate(
482
+ user_id: @user_id,
483
+ token_type: type
484
+ )
485
+
486
+ token_data = response.data['token_auth']
487
+
488
+ # Cache the token
489
+ @cache[cache_key] = {
490
+ token: token_data['token'],
491
+ expires_at: Time.parse(token_data['expires_at'])
492
+ }
493
+
494
+ token_data['token']
495
+ end
496
+
497
+ def token_expired?(expires_at)
498
+ # Consider token expired 5 minutes before actual expiry
499
+ expires_at < Time.now + 300
500
+ end
501
+ end
502
+
503
+ # Usage
504
+ manager = TokenManager.new("buyer-123")
505
+ token = manager.get_card_token # Generates new token
506
+ token2 = manager.get_card_token # Returns cached token if not expired
507
+ ```
508
+
509
+ ### Example: Audit Logging
510
+
511
+ ```ruby
512
+ class AuditedTokenGenerator
513
+ def self.generate(user_id:, token_type:, request_context: {})
514
+ start_time = Time.now
515
+
516
+ begin
517
+ response = ZaiPayment.token_auths.generate(
518
+ user_id: user_id,
519
+ token_type: token_type
520
+ )
521
+
522
+ # Log successful generation
523
+ log_token_generation(
524
+ user_id: user_id,
525
+ token_type: token_type,
526
+ success: true,
527
+ duration: Time.now - start_time,
528
+ context: request_context
529
+ )
530
+
531
+ response
532
+ rescue => e
533
+ # Log failed generation
534
+ log_token_generation(
535
+ user_id: user_id,
536
+ token_type: token_type,
537
+ success: false,
538
+ error: e.message,
539
+ duration: Time.now - start_time,
540
+ context: request_context
541
+ )
542
+
543
+ raise
544
+ end
545
+ end
546
+
547
+ def self.log_token_generation(data)
548
+ Rails.logger.info({
549
+ event: 'token_generation',
550
+ timestamp: Time.now.iso8601,
551
+ **data
552
+ }.to_json)
553
+
554
+ # Optionally store in database for audit trail
555
+ # AuditLog.create!(
556
+ # event_type: 'token_generation',
557
+ # user_id: data[:user_id],
558
+ # success: data[:success],
559
+ # metadata: data
560
+ # )
561
+ end
562
+ end
563
+
564
+ # Usage
565
+ response = AuditedTokenGenerator.generate(
566
+ user_id: "buyer-123",
567
+ token_type: "card",
568
+ request_context: {
569
+ ip_address: request.ip,
570
+ user_agent: request.user_agent,
571
+ session_id: session.id
572
+ }
573
+ )
574
+ ```
575
+
576
+ ### Example: Rate Limiting Protection
577
+
578
+ ```ruby
579
+ class RateLimitedTokenGenerator
580
+ def initialize(user_id)
581
+ @user_id = user_id
582
+ @redis = Redis.new
583
+ end
584
+
585
+ def generate(token_type:, limit: 10, window: 3600)
586
+ # Check rate limit
587
+ rate_limit_key = "token_gen:#{@user_id}:#{Time.now.to_i / window}"
588
+ current_count = @redis.incr(rate_limit_key)
589
+ @redis.expire(rate_limit_key, window)
590
+
591
+ if current_count > limit
592
+ raise "Rate limit exceeded: #{limit} tokens per #{window} seconds"
593
+ end
594
+
595
+ # Generate token
596
+ response = ZaiPayment.token_auths.generate(
597
+ user_id: @user_id,
598
+ token_type: token_type
599
+ )
600
+
601
+ response.data['token_auth']['token']
602
+ end
603
+ end
604
+
605
+ # Usage
606
+ generator = RateLimitedTokenGenerator.new("buyer-123")
607
+ token = generator.generate(token_type: "card", limit: 5, window: 3600)
608
+ ```
609
+
610
+ ## Complete Example: Payment Flow
611
+
612
+ ```ruby
613
+ # Complete example showing the full payment flow with token generation
614
+
615
+ class PaymentFlowService
616
+ def initialize(buyer_id, seller_id)
617
+ @buyer_id = buyer_id
618
+ @seller_id = seller_id
619
+ end
620
+
621
+ def process_payment(amount:, payment_method: :card)
622
+ # Step 1: Generate token for buyer to collect payment details
623
+ token_response = generate_payment_token(payment_method)
624
+
625
+ # Step 2: Return token to frontend for secure data collection
626
+ # (In real app, frontend would use PromisePay.js to collect details)
627
+
628
+ # Step 3: Create item for payment
629
+ item_response = create_payment_item(amount)
630
+
631
+ {
632
+ success: true,
633
+ token: token_response[:token],
634
+ item_id: item_response[:item_id],
635
+ message: "Ready to process payment"
636
+ }
637
+ rescue => e
638
+ {
639
+ success: false,
640
+ error: e.message
641
+ }
642
+ end
643
+
644
+ private
645
+
646
+ def generate_payment_token(payment_method)
647
+ token_type = payment_method == :card ? 'card' : 'bank'
648
+
649
+ response = ZaiPayment.token_auths.generate(
650
+ user_id: @buyer_id,
651
+ token_type: token_type
652
+ )
653
+
654
+ {
655
+ token: response.data['token_auth']['token'],
656
+ expires_at: response.data['token_auth']['expires_at']
657
+ }
658
+ end
659
+
660
+ def create_payment_item(amount)
661
+ response = ZaiPayment.items.create(
662
+ name: "Payment Transaction",
663
+ amount: amount,
664
+ payment_type: 2, # Credit card
665
+ buyer_id: @buyer_id,
666
+ seller_id: @seller_id
667
+ )
668
+
669
+ {
670
+ item_id: response.data['items']['id']
671
+ }
672
+ end
673
+ end
674
+
675
+ # Usage
676
+ service = PaymentFlowService.new("buyer-123", "seller-456")
677
+ result = service.process_payment(amount: 10000) # $100.00
678
+
679
+ if result[:success]
680
+ puts "Payment token: #{result[:token]}"
681
+ puts "Item ID: #{result[:item_id]}"
682
+ puts "Send token to frontend for payment collection"
683
+ else
684
+ puts "Error: #{result[:error]}"
685
+ end
686
+ ```
687
+