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.
@@ -0,0 +1,522 @@
1
+ # Usage Guide
2
+
3
+ This guide provides practical examples and best practices for integrating the RMA Payment Gateway into your Ruby application.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Quick Start](#quick-start)
8
+ - [Rails Integration](#rails-integration)
9
+ - [Sinatra Integration](#sinatra-integration)
10
+ - [Common Use Cases](#common-use-cases)
11
+ - [Best Practices](#best-practices)
12
+ - [Troubleshooting](#troubleshooting)
13
+
14
+ ## Quick Start
15
+
16
+ ### 1. Installation
17
+
18
+ Add to your Gemfile:
19
+
20
+ ```ruby
21
+ gem 'rma-payment-gateway'
22
+ ```
23
+
24
+ Run:
25
+
26
+ ```bash
27
+ bundle install
28
+ ```
29
+
30
+ ### 2. Configuration
31
+
32
+ Create a `.env` file:
33
+
34
+ ```env
35
+ RMA_BASE_URL=https://your-rma-gateway-url.com
36
+ RMA_RSA_KEY_PATH=/path/to/rsa_private_key.pem
37
+ RMA_BENEFICIARY_ID=your_beneficiary_id
38
+ RMA_PAYMENT_DESCRIPTION=Payment for services
39
+ ```
40
+
41
+ ### 3. Basic Usage
42
+
43
+ ```ruby
44
+ require 'rma/payment/gateway'
45
+
46
+ # Initialize client
47
+ client = Rma::Payment::Gateway::Client.new
48
+
49
+ # Process payment
50
+ begin
51
+ # Step 1: Authorization
52
+ auth_response = client.authorization.call("ORDER123", 100.50, "customer@email.com")
53
+ transaction_id = auth_response["bfs_bfsTxnId"]
54
+
55
+ # Step 2: Account Inquiry (customer provides bank details)
56
+ inquiry_response = client.account_inquiry.call(transaction_id, "1010", "12345678")
57
+
58
+ # Step 3: Debit Request (customer provides OTP)
59
+ debit_response = client.debit_request.call(transaction_id, "123456")
60
+
61
+ puts "Payment successful!"
62
+ rescue Rma::Payment::Gateway::Error => e
63
+ puts "Payment failed: #{e.message}"
64
+ end
65
+ ```
66
+
67
+ ## Rails Integration
68
+
69
+ ### Configuration
70
+
71
+ Create an initializer `config/initializers/rma_payment_gateway.rb`:
72
+
73
+ ```ruby
74
+ Rma::Payment::Gateway.configure do |config|
75
+ config.base_url = ENV['RMA_BASE_URL']
76
+ config.rsa_key_path = Rails.root.join('config', 'rma_private_key.pem').to_s
77
+ config.beneficiary_id = ENV['RMA_BENEFICIARY_ID']
78
+ config.payment_description = ENV['RMA_PAYMENT_DESCRIPTION']
79
+ config.timeout = 30
80
+ config.open_timeout = 10
81
+ end
82
+ ```
83
+
84
+ ### Service Object Pattern
85
+
86
+ Create a payment service `app/services/rma_payment_service.rb`:
87
+
88
+ ```ruby
89
+ class RmaPaymentService
90
+ attr_reader :client, :order, :errors
91
+
92
+ def initialize(order)
93
+ @order = order
94
+ @client = Rma::Payment::Gateway::Client.new
95
+ @errors = []
96
+ end
97
+
98
+ def authorize_payment
99
+ response = client.authorization.call(
100
+ order.order_number,
101
+ order.total_amount,
102
+ order.customer_email
103
+ )
104
+
105
+ order.update!(
106
+ rma_transaction_id: response["bfs_bfsTxnId"],
107
+ payment_status: 'authorized'
108
+ )
109
+
110
+ response
111
+ rescue Rma::Payment::Gateway::Error => e
112
+ @errors << e.message
113
+ Rails.logger.error("RMA Authorization failed: #{e.message}")
114
+ nil
115
+ end
116
+
117
+ def verify_account(bank_id, account_number)
118
+ response = client.account_inquiry.call(
119
+ order.rma_transaction_id,
120
+ bank_id,
121
+ account_number
122
+ )
123
+
124
+ order.update!(
125
+ customer_bank_id: bank_id,
126
+ customer_account_number: account_number,
127
+ customer_account_name: response["bfs_remitterName"],
128
+ payment_status: 'account_verified'
129
+ )
130
+
131
+ response
132
+ rescue Rma::Payment::Gateway::Error => e
133
+ @errors << e.message
134
+ Rails.logger.error("RMA Account Inquiry failed: #{e.message}")
135
+ nil
136
+ end
137
+
138
+ def complete_payment(otp)
139
+ response = client.debit_request.call(
140
+ order.rma_transaction_id,
141
+ otp
142
+ )
143
+
144
+ order.update!(
145
+ payment_status: 'completed',
146
+ paid_at: Time.current
147
+ )
148
+
149
+ response
150
+ rescue Rma::Payment::Gateway::Error => e
151
+ @errors << e.message
152
+ Rails.logger.error("RMA Debit Request failed: #{e.message}")
153
+ nil
154
+ end
155
+ end
156
+ ```
157
+
158
+ ### Controller Example
159
+
160
+ ```ruby
161
+ class PaymentsController < ApplicationController
162
+ before_action :set_order
163
+
164
+ def new
165
+ # Show payment form
166
+ end
167
+
168
+ def authorize
169
+ service = RmaPaymentService.new(@order)
170
+
171
+ if service.authorize_payment
172
+ redirect_to account_verification_path(@order), notice: 'Payment authorized'
173
+ else
174
+ flash[:error] = service.errors.join(', ')
175
+ render :new
176
+ end
177
+ end
178
+
179
+ def verify_account
180
+ service = RmaPaymentService.new(@order)
181
+
182
+ if service.verify_account(params[:bank_id], params[:account_number])
183
+ redirect_to otp_verification_path(@order), notice: 'OTP sent to your mobile'
184
+ else
185
+ flash[:error] = service.errors.join(', ')
186
+ render :account_form
187
+ end
188
+ end
189
+
190
+ def complete
191
+ service = RmaPaymentService.new(@order)
192
+
193
+ if service.complete_payment(params[:otp])
194
+ redirect_to order_path(@order), notice: 'Payment completed successfully'
195
+ else
196
+ flash[:error] = service.errors.join(', ')
197
+ render :otp_form
198
+ end
199
+ end
200
+
201
+ private
202
+
203
+ def set_order
204
+ @order = Order.find(params[:order_id])
205
+ end
206
+ end
207
+ ```
208
+
209
+ ### Background Job Example
210
+
211
+ For handling payment processing asynchronously:
212
+
213
+ ```ruby
214
+ class ProcessRmaPaymentJob < ApplicationJob
215
+ queue_as :payments
216
+
217
+ def perform(order_id, step, params = {})
218
+ order = Order.find(order_id)
219
+ service = RmaPaymentService.new(order)
220
+
221
+ case step
222
+ when 'authorize'
223
+ service.authorize_payment
224
+ when 'verify_account'
225
+ service.verify_account(params[:bank_id], params[:account_number])
226
+ when 'complete'
227
+ service.complete_payment(params[:otp])
228
+ end
229
+ rescue StandardError => e
230
+ Rails.logger.error("RMA Payment Job failed: #{e.message}")
231
+ # Send notification to admin
232
+ AdminMailer.payment_failed(order, e.message).deliver_later
233
+ end
234
+ end
235
+ ```
236
+
237
+ ## Sinatra Integration
238
+
239
+ ### Configuration
240
+
241
+ ```ruby
242
+ require 'sinatra'
243
+ require 'rma/payment/gateway'
244
+
245
+ configure do
246
+ Rma::Payment::Gateway.configure do |config|
247
+ config.base_url = ENV['RMA_BASE_URL']
248
+ config.rsa_key_path = File.join(settings.root, 'config', 'rma_private_key.pem')
249
+ config.beneficiary_id = ENV['RMA_BENEFICIARY_ID']
250
+ config.payment_description = ENV['RMA_PAYMENT_DESCRIPTION']
251
+ end
252
+ end
253
+
254
+ helpers do
255
+ def rma_client
256
+ @rma_client ||= Rma::Payment::Gateway::Client.new
257
+ end
258
+ end
259
+ ```
260
+
261
+ ### Routes Example
262
+
263
+ ```ruby
264
+ post '/payments/authorize' do
265
+ begin
266
+ response = rma_client.authorization.call(
267
+ params[:order_no],
268
+ params[:amount].to_f,
269
+ params[:email]
270
+ )
271
+
272
+ session[:transaction_id] = response["bfs_bfsTxnId"]
273
+ redirect '/payments/account-verification'
274
+ rescue Rma::Payment::Gateway::Error => e
275
+ flash[:error] = e.message
276
+ redirect '/payments/new'
277
+ end
278
+ end
279
+
280
+ post '/payments/verify-account' do
281
+ begin
282
+ response = rma_client.account_inquiry.call(
283
+ session[:transaction_id],
284
+ params[:bank_id],
285
+ params[:account_number]
286
+ )
287
+
288
+ session[:account_name] = response["bfs_remitterName"]
289
+ redirect '/payments/otp-verification'
290
+ rescue Rma::Payment::Gateway::Error => e
291
+ flash[:error] = e.message
292
+ redirect '/payments/account-verification'
293
+ end
294
+ end
295
+
296
+ post '/payments/complete' do
297
+ begin
298
+ response = rma_client.debit_request.call(
299
+ session[:transaction_id],
300
+ params[:otp]
301
+ )
302
+
303
+ flash[:success] = 'Payment completed successfully'
304
+ redirect '/orders/confirmation'
305
+ rescue Rma::Payment::Gateway::Error => e
306
+ flash[:error] = e.message
307
+ redirect '/payments/otp-verification'
308
+ end
309
+ end
310
+ ```
311
+
312
+ ## Common Use Cases
313
+
314
+ ### 1. E-commerce Checkout
315
+
316
+ ```ruby
317
+ class CheckoutService
318
+ def process_payment(cart, customer)
319
+ client = Rma::Payment::Gateway::Client.new
320
+
321
+ # Step 1: Create order
322
+ order = create_order(cart, customer)
323
+
324
+ # Step 2: Authorize payment
325
+ auth_response = client.authorization.call(
326
+ order.number,
327
+ order.total,
328
+ customer.email
329
+ )
330
+
331
+ order.update!(transaction_id: auth_response["bfs_bfsTxnId"])
332
+
333
+ # Return order for customer to complete payment
334
+ order
335
+ end
336
+ end
337
+ ```
338
+
339
+ ### 2. Subscription Payments
340
+
341
+ ```ruby
342
+ class SubscriptionPaymentService
343
+ def charge_subscription(subscription)
344
+ client = Rma::Payment::Gateway::Client.new
345
+
346
+ # Generate unique order number
347
+ order_no = "SUB-#{subscription.id}-#{Time.now.to_i}"
348
+
349
+ # Authorize payment
350
+ response = client.authorization.call(
351
+ order_no,
352
+ subscription.plan.price,
353
+ subscription.user.email
354
+ )
355
+
356
+ # Store transaction for later completion
357
+ subscription.payments.create!(
358
+ transaction_id: response["bfs_bfsTxnId"],
359
+ amount: subscription.plan.price,
360
+ status: 'pending'
361
+ )
362
+ end
363
+ end
364
+ ```
365
+
366
+ ### 3. Refund Handling
367
+
368
+ ```ruby
369
+ class RefundService
370
+ def process_refund(payment)
371
+ # Note: Refunds may need to be handled through RMA's admin interface
372
+ # This is a placeholder for your refund logic
373
+
374
+ payment.update!(
375
+ status: 'refund_requested',
376
+ refund_requested_at: Time.current
377
+ )
378
+
379
+ # Notify admin to process refund manually
380
+ AdminMailer.refund_requested(payment).deliver_later
381
+ end
382
+ end
383
+ ```
384
+
385
+ ## Best Practices
386
+
387
+ ### 1. Error Handling
388
+
389
+ Always handle specific exceptions:
390
+
391
+ ```ruby
392
+ begin
393
+ client.authorization.call(order_no, amount, email)
394
+ rescue Rma::Payment::Gateway::InvalidParameterError => e
395
+ # Handle validation errors - show to user
396
+ flash[:error] = "Invalid input: #{e.message}"
397
+ rescue Rma::Payment::Gateway::NetworkError => e
398
+ # Handle network errors - retry or show maintenance message
399
+ flash[:error] = "Service temporarily unavailable. Please try again."
400
+ rescue Rma::Payment::Gateway::Error => e
401
+ # Handle all other errors
402
+ flash[:error] = "Payment failed. Please contact support."
403
+ logger.error("RMA Payment Error: #{e.message}")
404
+ end
405
+ ```
406
+
407
+ ### 2. Logging
408
+
409
+ Log transactions with masked sensitive data:
410
+
411
+ ```ruby
412
+ def log_transaction(step, transaction_id, response)
413
+ Rails.logger.info({
414
+ step: step,
415
+ transaction_id: transaction_id,
416
+ response_code: response["bfs_responseCode"],
417
+ timestamp: Time.current
418
+ }.to_json)
419
+ end
420
+ ```
421
+
422
+ ### 3. Idempotency
423
+
424
+ Prevent duplicate payments:
425
+
426
+ ```ruby
427
+ def authorize_payment(order)
428
+ return if order.rma_transaction_id.present?
429
+
430
+ # Proceed with authorization
431
+ response = client.authorization.call(...)
432
+ order.update!(rma_transaction_id: response["bfs_bfsTxnId"])
433
+ end
434
+ ```
435
+
436
+ ### 4. Validation
437
+
438
+ Validate inputs before API calls:
439
+
440
+ ```ruby
441
+ def validate_payment_params(amount, email)
442
+ errors = []
443
+
444
+ unless Rma::Payment::Gateway::Utils.valid_amount?(amount)
445
+ errors << "Invalid amount"
446
+ end
447
+
448
+ unless Rma::Payment::Gateway::Utils.valid_email?(email)
449
+ errors << "Invalid email"
450
+ end
451
+
452
+ raise ArgumentError, errors.join(', ') if errors.any?
453
+ end
454
+ ```
455
+
456
+ ## Troubleshooting
457
+
458
+ ### Common Issues
459
+
460
+ #### 1. Configuration Error
461
+
462
+ **Error:** `Missing required configuration fields`
463
+
464
+ **Solution:**
465
+ ```ruby
466
+ # Check configuration
467
+ config = Rma::Payment::Gateway.configuration
468
+ puts config.missing_fields
469
+ ```
470
+
471
+ #### 2. Network Timeout
472
+
473
+ **Error:** `Network error: execution expired`
474
+
475
+ **Solution:**
476
+ ```ruby
477
+ # Increase timeout
478
+ Rma::Payment::Gateway.configure do |config|
479
+ config.timeout = 60
480
+ config.open_timeout = 20
481
+ end
482
+ ```
483
+
484
+ #### 3. Invalid OTP
485
+
486
+ **Error:** `Debit request failed: Invalid OTP`
487
+
488
+ **Solution:**
489
+ - Verify OTP is entered correctly
490
+ - Check if OTP has expired
491
+ - Restart the payment flow if needed
492
+
493
+ #### 4. Transaction Not Found
494
+
495
+ **Error:** `Invalid transaction`
496
+
497
+ **Solution:**
498
+ - Verify transaction ID is stored correctly
499
+ - Check if transaction has expired
500
+ - Ensure all three steps use the same transaction ID
501
+
502
+ ### Debug Mode
503
+
504
+ Enable detailed logging:
505
+
506
+ ```ruby
507
+ # In Rails
508
+ Rails.logger.level = :debug
509
+
510
+ # Log all requests/responses
511
+ client = Rma::Payment::Gateway::Client.new
512
+ response = client.authorization.call(...)
513
+ Rails.logger.debug("RMA Response: #{response.inspect}")
514
+ ```
515
+
516
+ ## Support
517
+
518
+ For additional help:
519
+
520
+ - GitHub Issues: https://github.com/dcplbt/rma-payment-gateway/issues
521
+ - Email: tashii.dendupp@gmail.com
522
+
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require "securerandom"
5
+
6
+ module Rma
7
+ module Payment
8
+ module Gateway
9
+ # Account Inquiry class for RMA Payment Gateway.
10
+ # Handles account inquiry for the RMA Payment Gateway client.
11
+ #
12
+ # @example
13
+ # auth = Rma::Payment::Gateway::AccountInquiry.new(client)
14
+ # auth.call(transaction_id, bank_id, account_no)
15
+ #
16
+ # @param client [Rma::Payment::Gateway::Client] Client instance
17
+ class AccountInquiry
18
+ attr_reader :client, :transaction_id, :bank_id, :account_no
19
+
20
+ def initialize(client)
21
+ @client = client
22
+ end
23
+
24
+ # Fetch account inquiry
25
+ # Returns the account inquiry response
26
+ def call(transaction_id, bank_id, account_no)
27
+ @transaction_id = transaction_id
28
+ @bank_id = bank_id
29
+ @account_no = account_no
30
+ response = client.post(
31
+ body: account_inquiry_request_body
32
+ )
33
+
34
+ validate_account_inquiry_response!(response)
35
+
36
+ response["result"]
37
+ rescue StandardError => e
38
+ raise AuthenticationError, "Failed to fetch account inquiry: #{e.message}"
39
+ end
40
+
41
+ private
42
+
43
+ def account_inquiry_request_body
44
+ params = {
45
+ bfs_bfsTxnId: transaction_id,
46
+ bfs_remitterBankId: bank_id,
47
+ bfs_remitterAccNo: account_no,
48
+ bfs_benfId: client.config.beneficiary_id,
49
+ bfs_msgType: "AE"
50
+ }
51
+
52
+ # Convert to URL-encoded format
53
+ URI.encode_www_form(params)
54
+ end
55
+
56
+ def validate_account_inquiry_response!(response)
57
+ unless response.is_a?(Hash) && response["result"]["bfs_responseCode"] == "00"
58
+ error_detail = response["result"]["bfs_responseDesc"] || "Unknown error"
59
+ raise AuthenticationError, "Account inquiry failed: #{error_detail}"
60
+ end
61
+
62
+ return if response["result"]
63
+
64
+ raise AuthenticationError, "No response data in response"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require "securerandom"
5
+
6
+ module Rma
7
+ module Payment
8
+ module Gateway
9
+ # Authorization class for RMA Payment Gateway.
10
+ # Handles authentication and authorization for the RMA Payment Gateway client.
11
+ #
12
+ # @example
13
+ # auth = Rma::Payment::Gateway::Authorization.new(client)
14
+ # auth.authorize
15
+ #
16
+ # @param client [Rma::Payment::Gateway::Client] Client instance
17
+ class Authorization
18
+ attr_reader :client, :order_no, :amount, :email
19
+
20
+ def initialize(client)
21
+ @client = client
22
+ end
23
+
24
+ # Fetch authorization token
25
+ # Returns the authorization response
26
+ def call(order_no, amount, email)
27
+ @order_no = order_no
28
+ @amount = amount
29
+ @email = email
30
+ validate_authorization_request!
31
+ response = client.post(body: authorization_request_body)
32
+
33
+ validate_authorization_response!(response)
34
+
35
+ response["result"]
36
+ rescue StandardError => e
37
+ raise AuthenticationError, "Failed to authorize: #{e.message}"
38
+ end
39
+
40
+ private
41
+
42
+ def authorization_request_body
43
+ params = {
44
+ bfs_benfTxnTime: Utils.generate_timestamp,
45
+ bfs_orderNo: order_no,
46
+ bfs_benfBankCode: "01",
47
+ bfs_txnCurrency: "BTN",
48
+ bfs_txnAmount: Utils.format_amount(amount),
49
+ bfs_remitterEmail: email,
50
+ bfs_paymentDesc: client.config.payment_description,
51
+ bfs_benfId: client.config.beneficiary_id,
52
+ bfs_msgType: "AR",
53
+ bfs_version: "5.0"
54
+ }
55
+
56
+ # Convert to URL-encoded format
57
+ URI.encode_www_form(params)
58
+ end
59
+
60
+ def validate_authorization_request!
61
+ raise InvalidParameterError, "Order number is required" if order_no.nil? || order_no.empty?
62
+ raise InvalidParameterError, "Amount is required" if amount.nil? || amount.empty?
63
+ raise InvalidParameterError, "Email is required" if email.nil? || email.empty?
64
+ raise InvalidParameterError, "Amount must be a number" unless Utils.valid_amount?(amount)
65
+ raise InvalidParameterError, "Email must be a valid email" unless Utils.valid_email?(email)
66
+ end
67
+
68
+ def validate_authorization_response!(response)
69
+ unless response.is_a?(Hash) && response["result"]["bfs_responseCode"] == "00"
70
+ error_detail = response["result"]["bfs_responseDesc"] || "Unknown error"
71
+ raise AuthenticationError, "Authorization failed: #{error_detail}"
72
+ end
73
+
74
+ return if response["result"]
75
+
76
+ raise AuthenticationError, "No response data in response"
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end