rma-payment-gateway 1.0.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 +7 -0
- data/CHANGELOG.md +60 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/README.md +446 -0
- data/Rakefile +12 -0
- data/docs/API.md +339 -0
- data/docs/EXAMPLES.md +650 -0
- data/docs/FLOW_DIAGRAM.md +440 -0
- data/docs/QUICK_REFERENCE.md +323 -0
- data/docs/README.md +259 -0
- data/docs/SECURITY.md +506 -0
- data/docs/USAGE_GUIDE.md +522 -0
- data/lib/rma/payment/gateway/account_inquiry.rb +69 -0
- data/lib/rma/payment/gateway/authorization.rb +81 -0
- data/lib/rma/payment/gateway/client.rb +124 -0
- data/lib/rma/payment/gateway/configuration.rb +42 -0
- data/lib/rma/payment/gateway/debit_request.rb +65 -0
- data/lib/rma/payment/gateway/errors.rb +42 -0
- data/lib/rma/payment/gateway/utils.rb +130 -0
- data/lib/rma/payment/gateway/version.rb +9 -0
- data/lib/rma/payment/gateway.rb +24 -0
- data/sig/rma/payment/gateway.rbs +8 -0
- metadata +65 -0
data/docs/EXAMPLES.md
ADDED
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
# Code Examples
|
|
2
|
+
|
|
3
|
+
This document provides practical code examples for common scenarios when using the RMA Payment Gateway gem.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Basic Examples](#basic-examples)
|
|
8
|
+
- [Rails Examples](#rails-examples)
|
|
9
|
+
- [Error Handling Examples](#error-handling-examples)
|
|
10
|
+
- [Testing Examples](#testing-examples)
|
|
11
|
+
- [Advanced Examples](#advanced-examples)
|
|
12
|
+
|
|
13
|
+
## Basic Examples
|
|
14
|
+
|
|
15
|
+
### Simple Payment Flow
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
require 'rma/payment/gateway'
|
|
19
|
+
|
|
20
|
+
# Initialize client
|
|
21
|
+
client = Rma::Payment::Gateway::Client.new
|
|
22
|
+
|
|
23
|
+
# Complete payment flow
|
|
24
|
+
def process_payment(order_no, amount, email, bank_id, account_no, otp)
|
|
25
|
+
# Step 1: Authorization
|
|
26
|
+
puts "Authorizing payment..."
|
|
27
|
+
auth_response = client.authorization.call(order_no, amount, email)
|
|
28
|
+
transaction_id = auth_response["bfs_bfsTxnId"]
|
|
29
|
+
puts "✓ Transaction ID: #{transaction_id}"
|
|
30
|
+
|
|
31
|
+
# Step 2: Account Inquiry
|
|
32
|
+
puts "Verifying account..."
|
|
33
|
+
inquiry_response = client.account_inquiry.call(transaction_id, bank_id, account_no)
|
|
34
|
+
puts "✓ Account holder: #{inquiry_response['bfs_remitterName']}"
|
|
35
|
+
|
|
36
|
+
# Step 3: Debit Request
|
|
37
|
+
puts "Completing payment..."
|
|
38
|
+
debit_response = client.debit_request.call(transaction_id, otp)
|
|
39
|
+
puts "✓ Payment successful!"
|
|
40
|
+
|
|
41
|
+
debit_response
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Usage
|
|
45
|
+
begin
|
|
46
|
+
result = process_payment(
|
|
47
|
+
"ORDER123",
|
|
48
|
+
100.50,
|
|
49
|
+
"customer@email.com",
|
|
50
|
+
"1010",
|
|
51
|
+
"12345678",
|
|
52
|
+
"123456"
|
|
53
|
+
)
|
|
54
|
+
puts "Payment completed: #{result['bfs_orderNo']}"
|
|
55
|
+
rescue Rma::Payment::Gateway::Error => e
|
|
56
|
+
puts "Payment failed: #{e.message}"
|
|
57
|
+
end
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Step-by-Step Payment
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
require 'rma/payment/gateway'
|
|
64
|
+
|
|
65
|
+
client = Rma::Payment::Gateway::Client.new
|
|
66
|
+
|
|
67
|
+
# Step 1: Authorization
|
|
68
|
+
puts "Enter order number:"
|
|
69
|
+
order_no = gets.chomp
|
|
70
|
+
|
|
71
|
+
puts "Enter amount:"
|
|
72
|
+
amount = gets.chomp.to_f
|
|
73
|
+
|
|
74
|
+
puts "Enter email:"
|
|
75
|
+
email = gets.chomp
|
|
76
|
+
|
|
77
|
+
auth_response = client.authorization.call(order_no, amount, email)
|
|
78
|
+
transaction_id = auth_response["bfs_bfsTxnId"]
|
|
79
|
+
puts "Authorization successful! Transaction ID: #{transaction_id}"
|
|
80
|
+
|
|
81
|
+
# Step 2: Account Inquiry
|
|
82
|
+
puts "\nEnter bank code (1010-1060):"
|
|
83
|
+
bank_id = gets.chomp
|
|
84
|
+
|
|
85
|
+
puts "Enter account number:"
|
|
86
|
+
account_no = gets.chomp
|
|
87
|
+
|
|
88
|
+
inquiry_response = client.account_inquiry.call(transaction_id, bank_id, account_no)
|
|
89
|
+
puts "Account verified: #{inquiry_response['bfs_remitterName']}"
|
|
90
|
+
puts "OTP sent to your registered mobile number"
|
|
91
|
+
|
|
92
|
+
# Step 3: Debit Request
|
|
93
|
+
puts "\nEnter OTP:"
|
|
94
|
+
otp = gets.chomp
|
|
95
|
+
|
|
96
|
+
debit_response = client.debit_request.call(transaction_id, otp)
|
|
97
|
+
puts "Payment successful!"
|
|
98
|
+
puts "Amount: #{debit_response['bfs_txnAmount']} BTN"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Rails Examples
|
|
102
|
+
|
|
103
|
+
### Payment Model
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
# app/models/payment.rb
|
|
107
|
+
class Payment < ApplicationRecord
|
|
108
|
+
belongs_to :order
|
|
109
|
+
belongs_to :user
|
|
110
|
+
|
|
111
|
+
enum status: {
|
|
112
|
+
pending: 0,
|
|
113
|
+
authorized: 1,
|
|
114
|
+
account_verified: 2,
|
|
115
|
+
completed: 3,
|
|
116
|
+
failed: 4,
|
|
117
|
+
refunded: 5
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
validates :order_id, presence: true
|
|
121
|
+
validates :amount, presence: true, numericality: { greater_than: 0 }
|
|
122
|
+
validates :transaction_id, uniqueness: true, allow_nil: true
|
|
123
|
+
|
|
124
|
+
def rma_client
|
|
125
|
+
@rma_client ||= Rma::Payment::Gateway::Client.new
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def authorize!
|
|
129
|
+
response = rma_client.authorization.call(
|
|
130
|
+
order.order_number,
|
|
131
|
+
amount,
|
|
132
|
+
user.email
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
update!(
|
|
136
|
+
transaction_id: response["bfs_bfsTxnId"],
|
|
137
|
+
status: :authorized,
|
|
138
|
+
authorized_at: Time.current
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
response
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def verify_account!(bank_id, account_number)
|
|
145
|
+
response = rma_client.account_inquiry.call(
|
|
146
|
+
transaction_id,
|
|
147
|
+
bank_id,
|
|
148
|
+
account_number
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
update!(
|
|
152
|
+
bank_id: bank_id,
|
|
153
|
+
account_number: account_number,
|
|
154
|
+
account_holder_name: response["bfs_remitterName"],
|
|
155
|
+
status: :account_verified,
|
|
156
|
+
account_verified_at: Time.current
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
response
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def complete!(otp)
|
|
163
|
+
response = rma_client.debit_request.call(transaction_id, otp)
|
|
164
|
+
|
|
165
|
+
update!(
|
|
166
|
+
status: :completed,
|
|
167
|
+
completed_at: Time.current
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Update order status
|
|
171
|
+
order.mark_as_paid!
|
|
172
|
+
|
|
173
|
+
response
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Payment Controller
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
# app/controllers/payments_controller.rb
|
|
182
|
+
class PaymentsController < ApplicationController
|
|
183
|
+
before_action :authenticate_user!
|
|
184
|
+
before_action :set_payment, only: [:show, :verify_account, :complete]
|
|
185
|
+
|
|
186
|
+
def new
|
|
187
|
+
@order = Order.find(params[:order_id])
|
|
188
|
+
@payment = @order.payments.build(
|
|
189
|
+
user: current_user,
|
|
190
|
+
amount: @order.total_amount
|
|
191
|
+
)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def create
|
|
195
|
+
@order = Order.find(params[:order_id])
|
|
196
|
+
@payment = @order.payments.build(payment_params)
|
|
197
|
+
@payment.user = current_user
|
|
198
|
+
|
|
199
|
+
if @payment.save
|
|
200
|
+
begin
|
|
201
|
+
@payment.authorize!
|
|
202
|
+
redirect_to verify_account_payment_path(@payment),
|
|
203
|
+
notice: 'Payment authorized. Please verify your account.'
|
|
204
|
+
rescue Rma::Payment::Gateway::Error => e
|
|
205
|
+
@payment.update(status: :failed, error_message: e.message)
|
|
206
|
+
flash.now[:error] = "Payment authorization failed: #{e.message}"
|
|
207
|
+
render :new
|
|
208
|
+
end
|
|
209
|
+
else
|
|
210
|
+
render :new
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def verify_account
|
|
215
|
+
# Show account verification form
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def submit_account
|
|
219
|
+
begin
|
|
220
|
+
@payment.verify_account!(
|
|
221
|
+
params[:bank_id],
|
|
222
|
+
params[:account_number]
|
|
223
|
+
)
|
|
224
|
+
redirect_to complete_payment_path(@payment),
|
|
225
|
+
notice: 'Account verified. OTP sent to your mobile.'
|
|
226
|
+
rescue Rma::Payment::Gateway::Error => e
|
|
227
|
+
flash.now[:error] = "Account verification failed: #{e.message}"
|
|
228
|
+
render :verify_account
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def complete
|
|
233
|
+
# Show OTP form
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def submit_otp
|
|
237
|
+
begin
|
|
238
|
+
@payment.complete!(params[:otp])
|
|
239
|
+
redirect_to order_path(@payment.order),
|
|
240
|
+
notice: 'Payment completed successfully!'
|
|
241
|
+
rescue Rma::Payment::Gateway::Error => e
|
|
242
|
+
flash.now[:error] = "Payment failed: #{e.message}"
|
|
243
|
+
render :complete
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
private
|
|
248
|
+
|
|
249
|
+
def set_payment
|
|
250
|
+
@payment = current_user.payments.find(params[:id])
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def payment_params
|
|
254
|
+
params.require(:payment).permit(:amount)
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Payment Service Object
|
|
260
|
+
|
|
261
|
+
```ruby
|
|
262
|
+
# app/services/payment_processor.rb
|
|
263
|
+
class PaymentProcessor
|
|
264
|
+
attr_reader :payment, :client, :errors
|
|
265
|
+
|
|
266
|
+
def initialize(payment)
|
|
267
|
+
@payment = payment
|
|
268
|
+
@client = Rma::Payment::Gateway::Client.new
|
|
269
|
+
@errors = []
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def authorize
|
|
273
|
+
validate_authorization!
|
|
274
|
+
|
|
275
|
+
response = client.authorization.call(
|
|
276
|
+
payment.order.order_number,
|
|
277
|
+
payment.amount,
|
|
278
|
+
payment.user.email
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
payment.update!(
|
|
282
|
+
transaction_id: response["bfs_bfsTxnId"],
|
|
283
|
+
status: 'authorized'
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
log_event('authorized', response)
|
|
287
|
+
notify_user('authorization_success')
|
|
288
|
+
|
|
289
|
+
true
|
|
290
|
+
rescue Rma::Payment::Gateway::Error => e
|
|
291
|
+
handle_error('authorization', e)
|
|
292
|
+
false
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def verify_account(bank_id, account_number)
|
|
296
|
+
validate_account_verification!
|
|
297
|
+
|
|
298
|
+
response = client.account_inquiry.call(
|
|
299
|
+
payment.transaction_id,
|
|
300
|
+
bank_id,
|
|
301
|
+
account_number
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
payment.update!(
|
|
305
|
+
bank_id: bank_id,
|
|
306
|
+
account_number: account_number,
|
|
307
|
+
account_holder_name: response["bfs_remitterName"],
|
|
308
|
+
status: 'account_verified'
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
log_event('account_verified', response)
|
|
312
|
+
notify_user('otp_sent')
|
|
313
|
+
|
|
314
|
+
true
|
|
315
|
+
rescue Rma::Payment::Gateway::Error => e
|
|
316
|
+
handle_error('account_verification', e)
|
|
317
|
+
false
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def complete(otp)
|
|
321
|
+
validate_completion!
|
|
322
|
+
|
|
323
|
+
response = client.debit_request.call(
|
|
324
|
+
payment.transaction_id,
|
|
325
|
+
otp
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
payment.update!(
|
|
329
|
+
status: 'completed',
|
|
330
|
+
completed_at: Time.current
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
payment.order.mark_as_paid!
|
|
334
|
+
|
|
335
|
+
log_event('completed', response)
|
|
336
|
+
notify_user('payment_success')
|
|
337
|
+
send_receipt
|
|
338
|
+
|
|
339
|
+
true
|
|
340
|
+
rescue Rma::Payment::Gateway::Error => e
|
|
341
|
+
handle_error('completion', e)
|
|
342
|
+
false
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
private
|
|
346
|
+
|
|
347
|
+
def validate_authorization!
|
|
348
|
+
raise ArgumentError, "Payment already authorized" if payment.authorized?
|
|
349
|
+
raise ArgumentError, "Invalid amount" unless valid_amount?
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def validate_account_verification!
|
|
353
|
+
raise ArgumentError, "Payment not authorized" unless payment.authorized?
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def validate_completion!
|
|
357
|
+
raise ArgumentError, "Account not verified" unless payment.account_verified?
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def valid_amount?
|
|
361
|
+
Rma::Payment::Gateway::Utils.valid_amount?(payment.amount)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def handle_error(step, error)
|
|
365
|
+
@errors << error.message
|
|
366
|
+
payment.update(
|
|
367
|
+
status: 'failed',
|
|
368
|
+
error_message: error.message,
|
|
369
|
+
failed_at: Time.current
|
|
370
|
+
)
|
|
371
|
+
log_event("#{step}_failed", { error: error.message })
|
|
372
|
+
notify_admin_of_failure(step, error)
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def log_event(event, data)
|
|
376
|
+
PaymentLog.create!(
|
|
377
|
+
payment: payment,
|
|
378
|
+
event: event,
|
|
379
|
+
data: data.to_json,
|
|
380
|
+
created_at: Time.current
|
|
381
|
+
)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def notify_user(template)
|
|
385
|
+
PaymentMailer.send(template, payment).deliver_later
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def notify_admin_of_failure(step, error)
|
|
389
|
+
AdminMailer.payment_failed(payment, step, error).deliver_later
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def send_receipt
|
|
393
|
+
PaymentMailer.receipt(payment).deliver_later
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## Error Handling Examples
|
|
399
|
+
|
|
400
|
+
### Comprehensive Error Handling
|
|
401
|
+
|
|
402
|
+
```ruby
|
|
403
|
+
def process_payment_with_error_handling(order_no, amount, email)
|
|
404
|
+
client = Rma::Payment::Gateway::Client.new
|
|
405
|
+
|
|
406
|
+
begin
|
|
407
|
+
response = client.authorization.call(order_no, amount, email)
|
|
408
|
+
{ success: true, data: response }
|
|
409
|
+
|
|
410
|
+
rescue Rma::Payment::Gateway::ConfigurationError => e
|
|
411
|
+
# Configuration issue - needs admin attention
|
|
412
|
+
log_error("Configuration error", e)
|
|
413
|
+
notify_admin(e)
|
|
414
|
+
{ success: false, error: "System configuration error. Please contact support." }
|
|
415
|
+
|
|
416
|
+
rescue Rma::Payment::Gateway::InvalidParameterError => e
|
|
417
|
+
# User input error - show to user
|
|
418
|
+
log_error("Invalid parameters", e)
|
|
419
|
+
{ success: false, error: "Invalid input: #{e.message}" }
|
|
420
|
+
|
|
421
|
+
rescue Rma::Payment::Gateway::AuthenticationError => e
|
|
422
|
+
# Authentication failed - could be temporary
|
|
423
|
+
log_error("Authentication error", e)
|
|
424
|
+
{ success: false, error: "Payment authorization failed. Please try again." }
|
|
425
|
+
|
|
426
|
+
rescue Rma::Payment::Gateway::NetworkError => e
|
|
427
|
+
# Network issue - retry might help
|
|
428
|
+
log_error("Network error", e)
|
|
429
|
+
{ success: false, error: "Service temporarily unavailable. Please try again later." }
|
|
430
|
+
|
|
431
|
+
rescue Rma::Payment::Gateway::APIError => e
|
|
432
|
+
# API error - log and notify
|
|
433
|
+
log_error("API error", e)
|
|
434
|
+
notify_admin(e)
|
|
435
|
+
{ success: false, error: "Payment processing error. Please contact support." }
|
|
436
|
+
|
|
437
|
+
rescue Rma::Payment::Gateway::Error => e
|
|
438
|
+
# Generic error - catch all
|
|
439
|
+
log_error("Unknown error", e)
|
|
440
|
+
notify_admin(e)
|
|
441
|
+
{ success: false, error: "An unexpected error occurred. Please contact support." }
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Retry Logic
|
|
447
|
+
|
|
448
|
+
```ruby
|
|
449
|
+
def authorize_with_retry(order_no, amount, email, max_retries: 3)
|
|
450
|
+
client = Rma::Payment::Gateway::Client.new
|
|
451
|
+
retries = 0
|
|
452
|
+
|
|
453
|
+
begin
|
|
454
|
+
client.authorization.call(order_no, amount, email)
|
|
455
|
+
|
|
456
|
+
rescue Rma::Payment::Gateway::NetworkError => e
|
|
457
|
+
retries += 1
|
|
458
|
+
if retries < max_retries
|
|
459
|
+
sleep(2 ** retries) # Exponential backoff
|
|
460
|
+
retry
|
|
461
|
+
else
|
|
462
|
+
raise e
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
rescue Rma::Payment::Gateway::InvalidParameterError => e
|
|
466
|
+
# Don't retry validation errors
|
|
467
|
+
raise e
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
## Testing Examples
|
|
473
|
+
|
|
474
|
+
### RSpec Examples
|
|
475
|
+
|
|
476
|
+
```ruby
|
|
477
|
+
# spec/services/payment_processor_spec.rb
|
|
478
|
+
require 'rails_helper'
|
|
479
|
+
|
|
480
|
+
RSpec.describe PaymentProcessor do
|
|
481
|
+
let(:user) { create(:user, email: 'test@example.com') }
|
|
482
|
+
let(:order) { create(:order, order_number: 'ORDER123') }
|
|
483
|
+
let(:payment) { create(:payment, user: user, order: order, amount: 100.50) }
|
|
484
|
+
let(:processor) { described_class.new(payment) }
|
|
485
|
+
let(:client) { instance_double(Rma::Payment::Gateway::Client) }
|
|
486
|
+
|
|
487
|
+
before do
|
|
488
|
+
allow(Rma::Payment::Gateway::Client).to receive(:new).and_return(client)
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
describe '#authorize' do
|
|
492
|
+
let(:auth_response) do
|
|
493
|
+
{
|
|
494
|
+
"bfs_bfsTxnId" => "TXN123",
|
|
495
|
+
"bfs_responseCode" => "00",
|
|
496
|
+
"bfs_responseDesc" => "Success"
|
|
497
|
+
}
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
context 'when successful' do
|
|
501
|
+
before do
|
|
502
|
+
allow(client).to receive_message_chain(:authorization, :call)
|
|
503
|
+
.and_return(auth_response)
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
it 'authorizes the payment' do
|
|
507
|
+
expect(processor.authorize).to be true
|
|
508
|
+
expect(payment.reload.transaction_id).to eq("TXN123")
|
|
509
|
+
expect(payment.status).to eq('authorized')
|
|
510
|
+
end
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
context 'when authorization fails' do
|
|
514
|
+
before do
|
|
515
|
+
allow(client).to receive_message_chain(:authorization, :call)
|
|
516
|
+
.and_raise(Rma::Payment::Gateway::AuthenticationError, "Failed")
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
it 'handles the error' do
|
|
520
|
+
expect(processor.authorize).to be false
|
|
521
|
+
expect(processor.errors).to include("Failed")
|
|
522
|
+
expect(payment.reload.status).to eq('failed')
|
|
523
|
+
end
|
|
524
|
+
end
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Mock Responses
|
|
530
|
+
|
|
531
|
+
```ruby
|
|
532
|
+
# spec/support/rma_helpers.rb
|
|
533
|
+
module RmaHelpers
|
|
534
|
+
def mock_rma_authorization(transaction_id: "TXN123")
|
|
535
|
+
{
|
|
536
|
+
"bfs_bfsTxnId" => transaction_id,
|
|
537
|
+
"bfs_responseCode" => "00",
|
|
538
|
+
"bfs_responseDesc" => "Success",
|
|
539
|
+
"bfs_orderNo" => "ORDER123",
|
|
540
|
+
"bfs_txnAmount" => "100.50"
|
|
541
|
+
}
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
def mock_rma_account_inquiry(name: "John Doe")
|
|
545
|
+
{
|
|
546
|
+
"bfs_bfsTxnId" => "TXN123",
|
|
547
|
+
"bfs_responseCode" => "00",
|
|
548
|
+
"bfs_responseDesc" => "Success",
|
|
549
|
+
"bfs_remitterName" => name,
|
|
550
|
+
"bfs_remitterAccNo" => "12345678"
|
|
551
|
+
}
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
def mock_rma_debit_request
|
|
555
|
+
{
|
|
556
|
+
"bfs_bfsTxnId" => "TXN123",
|
|
557
|
+
"bfs_responseCode" => "00",
|
|
558
|
+
"bfs_responseDesc" => "Transaction Successful",
|
|
559
|
+
"bfs_txnAmount" => "100.50",
|
|
560
|
+
"bfs_orderNo" => "ORDER123"
|
|
561
|
+
}
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
RSpec.configure do |config|
|
|
566
|
+
config.include RmaHelpers
|
|
567
|
+
end
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
## Advanced Examples
|
|
571
|
+
|
|
572
|
+
### Background Job Processing
|
|
573
|
+
|
|
574
|
+
```ruby
|
|
575
|
+
# app/jobs/process_payment_job.rb
|
|
576
|
+
class ProcessPaymentJob < ApplicationJob
|
|
577
|
+
queue_as :payments
|
|
578
|
+
retry_on Rma::Payment::Gateway::NetworkError, wait: :exponentially_longer, attempts: 5
|
|
579
|
+
discard_on Rma::Payment::Gateway::InvalidParameterError
|
|
580
|
+
|
|
581
|
+
def perform(payment_id, step, params = {})
|
|
582
|
+
payment = Payment.find(payment_id)
|
|
583
|
+
processor = PaymentProcessor.new(payment)
|
|
584
|
+
|
|
585
|
+
case step
|
|
586
|
+
when 'authorize'
|
|
587
|
+
processor.authorize
|
|
588
|
+
when 'verify_account'
|
|
589
|
+
processor.verify_account(params[:bank_id], params[:account_number])
|
|
590
|
+
when 'complete'
|
|
591
|
+
processor.complete(params[:otp])
|
|
592
|
+
end
|
|
593
|
+
rescue Rma::Payment::Gateway::Error => e
|
|
594
|
+
payment.update(status: 'failed', error_message: e.message)
|
|
595
|
+
raise
|
|
596
|
+
end
|
|
597
|
+
end
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### Webhook Handler
|
|
601
|
+
|
|
602
|
+
```ruby
|
|
603
|
+
# app/controllers/webhooks/rma_controller.rb
|
|
604
|
+
module Webhooks
|
|
605
|
+
class RmaController < ApplicationController
|
|
606
|
+
skip_before_action :verify_authenticity_token
|
|
607
|
+
|
|
608
|
+
def payment_status
|
|
609
|
+
# Handle payment status webhook from RMA
|
|
610
|
+
transaction_id = params[:transaction_id]
|
|
611
|
+
status = params[:status]
|
|
612
|
+
|
|
613
|
+
payment = Payment.find_by(transaction_id: transaction_id)
|
|
614
|
+
|
|
615
|
+
if payment
|
|
616
|
+
payment.update_from_webhook(status, params)
|
|
617
|
+
head :ok
|
|
618
|
+
else
|
|
619
|
+
head :not_found
|
|
620
|
+
end
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
### Payment Analytics
|
|
627
|
+
|
|
628
|
+
```ruby
|
|
629
|
+
# app/services/payment_analytics.rb
|
|
630
|
+
class PaymentAnalytics
|
|
631
|
+
def self.success_rate(period: 30.days)
|
|
632
|
+
total = Payment.where('created_at > ?', period.ago).count
|
|
633
|
+
successful = Payment.completed.where('created_at > ?', period.ago).count
|
|
634
|
+
|
|
635
|
+
return 0 if total.zero?
|
|
636
|
+
(successful.to_f / total * 100).round(2)
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
def self.average_completion_time
|
|
640
|
+
Payment.completed.average('EXTRACT(EPOCH FROM (completed_at - created_at))')
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
def self.failure_reasons
|
|
644
|
+
Payment.failed.group(:error_message).count
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
For more examples, see the [Usage Guide](USAGE_GUIDE.md).
|
|
650
|
+
|