dk_payment_gateway 1.0.2 → 1.0.3
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/Gemfile.lock +1 -1
- data/TESTING.md +137 -0
- data/examples/generate_qr.rb +32 -33
- data/examples/intra_transfer.rb +36 -38
- data/lib/dk_payment_gateway/intra_transaction.rb +6 -8
- data/lib/dk_payment_gateway/pull_payment.rb +3 -4
- data/lib/dk_payment_gateway/qr_payment.rb +5 -5
- data/lib/dk_payment_gateway/signature.rb +9 -6
- data/lib/dk_payment_gateway/transaction_status.rb +25 -11
- data/lib/dk_payment_gateway/version.rb +1 -1
- data/simple_test.rb +80 -0
- data/verify_fix.rb +45 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 84d92010d9555cd207609fdb2c65abc51ff444b388c9ff58631622df439b0bc6
|
|
4
|
+
data.tar.gz: 8b3b080e9f3f61233ade765850487c924d5e79df81985a8e1345a4ab07142e9f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 386dd822005554f4fe7524eff2c178bf40f83ec02d83cc33d144dcbfc9c09fadb77215bdf905eab2045d4f000d1da131c0dfcef3caf26296d39fb28071203e37
|
|
7
|
+
data.tar.gz: 87ab8f1d65f239ddb74e875de79622b1c8000090068885476ebbba5340bfecebeda18874295ed1acfc695e8a79de57dd4d8f79ee169cdb365acf07828dc00152
|
data/Gemfile.lock
CHANGED
data/TESTING.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# DK Payment Gateway Testing
|
|
2
|
+
|
|
3
|
+
This document describes the testing structure and approach for the DK Payment Gateway Ruby gem.
|
|
4
|
+
|
|
5
|
+
## Test Structure
|
|
6
|
+
|
|
7
|
+
The test suite is organized into multiple files to ensure comprehensive coverage:
|
|
8
|
+
|
|
9
|
+
1. **spec/comprehensive_test_suite.rb** - Complete integration tests covering all aspects of the gem
|
|
10
|
+
2. **spec/core_functionality_tests.rb** - Core functionality tests focusing on essential methods and patterns
|
|
11
|
+
3. **spec/method_signature_tests.rb** - Tests specifically for documented method signatures
|
|
12
|
+
4. **spec/configuration_spec.rb** - Tests for configuration and initialization
|
|
13
|
+
5. **spec/dk_payment_gateway_spec.rb** - Main gem specification tests
|
|
14
|
+
|
|
15
|
+
## Running Tests
|
|
16
|
+
|
|
17
|
+
### Prerequisites
|
|
18
|
+
|
|
19
|
+
Ensure you have the required dependencies in your Gemfile:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
group :test do
|
|
23
|
+
gem 'rspec'
|
|
24
|
+
gem 'dk_payment_gateway'
|
|
25
|
+
end
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Running All Tests
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
rspec spec/
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Running Individual Test Files
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
rspec spec/comprehensive_test_suite.rb
|
|
38
|
+
rspec spec/core_functionality_tests.rb
|
|
39
|
+
rspec spec/method_signature_tests.rb
|
|
40
|
+
rspec spec/configuration_spec.rb
|
|
41
|
+
rspec spec/dk_payment_gateway_spec.rb
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Test Coverage
|
|
45
|
+
|
|
46
|
+
The tests cover the following areas:
|
|
47
|
+
|
|
48
|
+
### Configuration
|
|
49
|
+
|
|
50
|
+
- Ensures the gem can be properly configured with all required parameters
|
|
51
|
+
- Tests default configuration when not explicitly set
|
|
52
|
+
- Validates configuration values are correctly stored and retrieved
|
|
53
|
+
|
|
54
|
+
### Client Creation
|
|
55
|
+
|
|
56
|
+
- Tests that client instances are properly created
|
|
57
|
+
- Verifies client contains all required sub-modules
|
|
58
|
+
- Ensures proper object composition
|
|
59
|
+
|
|
60
|
+
### Method Existence
|
|
61
|
+
|
|
62
|
+
- Verifies all documented methods are available
|
|
63
|
+
- Tests method signatures for proper parameter handling
|
|
64
|
+
- Ensures all sub-modules are accessible through the client
|
|
65
|
+
|
|
66
|
+
### STAN Generation
|
|
67
|
+
|
|
68
|
+
- Tests the STAN number generation functionality
|
|
69
|
+
- Validates that different prefixes generate different STAN numbers
|
|
70
|
+
- Ensures STAN numbers are properly formatted
|
|
71
|
+
|
|
72
|
+
### Operations
|
|
73
|
+
|
|
74
|
+
- Pull Payment Operations (authorize, debit)
|
|
75
|
+
- Intra-Bank Transaction Operations (account_inquiry, fund_transfer)
|
|
76
|
+
- QR Payment Operations (generate_qr, save_qr_image)
|
|
77
|
+
- Transaction Status Operations (check_current_day, check_previous_days)
|
|
78
|
+
|
|
79
|
+
### Authentication
|
|
80
|
+
|
|
81
|
+
- Tests authentication method availability
|
|
82
|
+
- Ensures authentication can be called without errors
|
|
83
|
+
|
|
84
|
+
### Error Handling
|
|
85
|
+
|
|
86
|
+
- Ensures proper exception classes are defined
|
|
87
|
+
- Tests exception inheritance
|
|
88
|
+
- Validates all required error types are available
|
|
89
|
+
|
|
90
|
+
### Integration
|
|
91
|
+
|
|
92
|
+
- Tests the complete flow from initialization to method execution
|
|
93
|
+
- Verifies all components work together properly
|
|
94
|
+
|
|
95
|
+
## Test Types
|
|
96
|
+
|
|
97
|
+
1. **Unit Tests** - Individual method existence and basic functionality
|
|
98
|
+
2. **Integration Tests** - Complete flow testing
|
|
99
|
+
3. **Error Handling Tests** - Exception class verification
|
|
100
|
+
4. **Signature Tests** - Method parameter validation
|
|
101
|
+
|
|
102
|
+
## Contributing Tests
|
|
103
|
+
|
|
104
|
+
When adding new features or modifying existing ones, please add corresponding tests to ensure:
|
|
105
|
+
|
|
106
|
+
1. All new methods are properly tested
|
|
107
|
+
2. Error conditions are handled correctly
|
|
108
|
+
3. Integration flows work as expected
|
|
109
|
+
4. Backward compatibility is maintained
|
|
110
|
+
|
|
111
|
+
## Note on Mocking
|
|
112
|
+
|
|
113
|
+
For actual API interaction testing, you would typically use mocking libraries like `webmock` or `vcr` to simulate API responses without making real network calls.
|
|
114
|
+
|
|
115
|
+
## Rake Integration
|
|
116
|
+
|
|
117
|
+
The project includes a Rakefile that sets up the default spec task:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
require "bundler/gem_tasks"
|
|
121
|
+
require "rspec/core/rake_task"
|
|
122
|
+
|
|
123
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
124
|
+
task default: :spec
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
This allows running tests with simply:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
rake spec
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
or
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
rake
|
|
137
|
+
```
|
data/examples/generate_qr.rb
CHANGED
|
@@ -9,46 +9,46 @@ require 'dk_payment_gateway'
|
|
|
9
9
|
|
|
10
10
|
# Configure the client
|
|
11
11
|
DkPaymentGateway.configure do |config|
|
|
12
|
-
config.base_url = ENV['DK_BASE_URL'] ||
|
|
12
|
+
config.base_url = ENV['DK_BASE_URL'] || 'https://internal-gateway.uat.digitalkidu.bt/api/dkpg'
|
|
13
13
|
config.api_key = ENV['DK_API_KEY']
|
|
14
14
|
config.username = ENV['DK_USERNAME']
|
|
15
15
|
config.password = ENV['DK_PASSWORD']
|
|
16
16
|
config.client_id = ENV['DK_CLIENT_ID']
|
|
17
17
|
config.client_secret = ENV['DK_CLIENT_SECRET']
|
|
18
|
-
config.source_app = ENV['DK_SOURCE_APP'] ||
|
|
18
|
+
config.source_app = ENV['DK_SOURCE_APP'] || 'SRC_AVS_0201'
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def main
|
|
22
|
-
puts
|
|
22
|
+
puts '=== DK Payment Gateway - QR Code Generation Example ==='
|
|
23
23
|
puts
|
|
24
24
|
|
|
25
25
|
# Initialize client
|
|
26
26
|
client = DkPaymentGateway.client
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
# Authenticate
|
|
29
|
-
puts
|
|
29
|
+
puts 'Authenticating...'
|
|
30
30
|
client.authenticate!
|
|
31
|
-
puts
|
|
31
|
+
puts '✓ Authentication successful'
|
|
32
32
|
puts
|
|
33
33
|
|
|
34
34
|
# Merchant details
|
|
35
|
-
merchant_account =
|
|
36
|
-
|
|
37
|
-
puts
|
|
38
|
-
puts
|
|
39
|
-
puts
|
|
40
|
-
print
|
|
35
|
+
merchant_account = '100100148337'
|
|
36
|
+
|
|
37
|
+
puts 'Select QR type:'
|
|
38
|
+
puts '1. Static QR (customer enters amount)'
|
|
39
|
+
puts '2. Dynamic QR (fixed amount)'
|
|
40
|
+
print 'Choice (1 or 2): '
|
|
41
41
|
choice = gets.chomp
|
|
42
42
|
puts
|
|
43
43
|
|
|
44
44
|
begin
|
|
45
45
|
case choice
|
|
46
|
-
when
|
|
46
|
+
when '1'
|
|
47
47
|
generate_static_qr(client, merchant_account)
|
|
48
|
-
when
|
|
48
|
+
when '2'
|
|
49
49
|
generate_dynamic_qr(client, merchant_account)
|
|
50
50
|
else
|
|
51
|
-
puts
|
|
51
|
+
puts 'Invalid choice'
|
|
52
52
|
end
|
|
53
53
|
rescue DkPaymentGateway::Error => e
|
|
54
54
|
puts "✗ Error: #{e.message}"
|
|
@@ -56,55 +56,54 @@ def main
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def generate_static_qr(client, merchant_account)
|
|
59
|
-
puts
|
|
59
|
+
puts 'Generating Static QR Code...'
|
|
60
60
|
puts " Merchant Account: #{merchant_account}"
|
|
61
|
-
puts
|
|
61
|
+
puts ' Amount: Customer will enter'
|
|
62
62
|
puts
|
|
63
63
|
|
|
64
64
|
response = client.qr_payment.generate_qr(
|
|
65
|
-
request_id: DkPaymentGateway::Utils.generate_request_id(
|
|
66
|
-
currency:
|
|
65
|
+
request_id: DkPaymentGateway::Utils.generate_request_id('QR'),
|
|
66
|
+
currency: 'BTN',
|
|
67
67
|
bene_account_number: merchant_account,
|
|
68
68
|
amount: 0, # 0 = static QR
|
|
69
|
-
mcc_code:
|
|
70
|
-
remarks:
|
|
69
|
+
mcc_code: '5411', # Grocery store
|
|
70
|
+
remarks: 'Payment to merchant'
|
|
71
71
|
)
|
|
72
72
|
|
|
73
73
|
filename = "static_qr_#{Time.now.to_i}.png"
|
|
74
74
|
client.qr_payment.save_qr_image(response['image'], filename)
|
|
75
|
-
|
|
76
|
-
puts
|
|
75
|
+
|
|
76
|
+
puts '✓ Static QR code generated successfully!'
|
|
77
77
|
puts " Saved to: #{filename}"
|
|
78
|
-
puts
|
|
78
|
+
puts ' Customers can scan this QR and enter any amount'
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def generate_dynamic_qr(client, merchant_account)
|
|
82
|
-
print
|
|
82
|
+
print 'Enter amount (BTN): '
|
|
83
83
|
amount = gets.chomp.to_f
|
|
84
84
|
puts
|
|
85
85
|
|
|
86
|
-
puts
|
|
86
|
+
puts 'Generating Dynamic QR Code...'
|
|
87
87
|
puts " Merchant Account: #{merchant_account}"
|
|
88
88
|
puts " Amount: BTN #{DkPaymentGateway::Utils.format_amount(amount)}"
|
|
89
89
|
puts
|
|
90
90
|
|
|
91
91
|
response = client.qr_payment.generate_qr(
|
|
92
|
-
request_id: DkPaymentGateway::Utils.generate_request_id(
|
|
93
|
-
currency:
|
|
92
|
+
request_id: DkPaymentGateway::Utils.generate_request_id('QR'),
|
|
93
|
+
currency: 'BTN',
|
|
94
94
|
bene_account_number: merchant_account,
|
|
95
95
|
amount: amount,
|
|
96
|
-
mcc_code:
|
|
97
|
-
remarks:
|
|
96
|
+
mcc_code: '5812', # Restaurant
|
|
97
|
+
remarks: 'Invoice payment'
|
|
98
98
|
)
|
|
99
99
|
|
|
100
100
|
filename = "dynamic_qr_#{amount.to_i}_#{Time.now.to_i}.png"
|
|
101
101
|
client.qr_payment.save_qr_image(response['image'], filename)
|
|
102
|
-
|
|
103
|
-
puts
|
|
102
|
+
|
|
103
|
+
puts '✓ Dynamic QR code generated successfully!'
|
|
104
104
|
puts " Saved to: #{filename}"
|
|
105
105
|
puts " Amount is fixed at BTN #{DkPaymentGateway::Utils.format_amount(amount)}"
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
# Run the example
|
|
109
109
|
main if __FILE__ == $PROGRAM_NAME
|
|
110
|
-
|
data/examples/intra_transfer.rb
CHANGED
|
@@ -9,34 +9,34 @@ require 'dk_payment_gateway'
|
|
|
9
9
|
|
|
10
10
|
# Configure the client
|
|
11
11
|
DkPaymentGateway.configure do |config|
|
|
12
|
-
config.base_url = ENV['DK_BASE_URL'] ||
|
|
12
|
+
config.base_url = ENV['DK_BASE_URL'] || 'https://internal-gateway.uat.digitalkidu.bt/api/dkpg'
|
|
13
13
|
config.api_key = ENV['DK_API_KEY']
|
|
14
14
|
config.username = ENV['DK_USERNAME']
|
|
15
15
|
config.password = ENV['DK_PASSWORD']
|
|
16
16
|
config.client_id = ENV['DK_CLIENT_ID']
|
|
17
17
|
config.client_secret = ENV['DK_CLIENT_SECRET']
|
|
18
|
-
config.source_app = ENV['DK_SOURCE_APP'] ||
|
|
18
|
+
config.source_app = ENV['DK_SOURCE_APP'] || 'SRC_AVS_0201'
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def main
|
|
22
|
-
puts
|
|
22
|
+
puts '=== DK Payment Gateway - Intra-Bank Transfer Example ==='
|
|
23
23
|
puts
|
|
24
24
|
|
|
25
25
|
# Initialize client
|
|
26
26
|
client = DkPaymentGateway.client
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
# Authenticate
|
|
29
|
-
puts
|
|
29
|
+
puts 'Authenticating...'
|
|
30
30
|
client.authenticate!
|
|
31
|
-
puts
|
|
31
|
+
puts '✓ Authentication successful'
|
|
32
32
|
puts
|
|
33
33
|
|
|
34
34
|
# Transfer details
|
|
35
|
-
source_account =
|
|
36
|
-
beneficiary_account =
|
|
35
|
+
source_account = '100100365856'
|
|
36
|
+
beneficiary_account = '100100148337'
|
|
37
37
|
amount = 500.00
|
|
38
|
-
|
|
39
|
-
puts
|
|
38
|
+
|
|
39
|
+
puts 'Transfer Details:'
|
|
40
40
|
puts " From: #{source_account}"
|
|
41
41
|
puts " To: #{beneficiary_account}"
|
|
42
42
|
puts " Amount: BTN #{amount}"
|
|
@@ -44,63 +44,62 @@ def main
|
|
|
44
44
|
|
|
45
45
|
begin
|
|
46
46
|
# Step 1: Verify beneficiary account
|
|
47
|
-
puts
|
|
48
|
-
|
|
47
|
+
puts 'Step 1: Verifying beneficiary account...'
|
|
48
|
+
|
|
49
49
|
inquiry_response = client.intra_transaction.account_inquiry(
|
|
50
|
-
request_id: DkPaymentGateway::Utils.generate_request_id(
|
|
50
|
+
request_id: DkPaymentGateway::Utils.generate_request_id('INQ'),
|
|
51
51
|
amount: amount,
|
|
52
|
-
currency:
|
|
53
|
-
bene_bank_code:
|
|
52
|
+
currency: 'BTN',
|
|
53
|
+
bene_bank_code: '1060',
|
|
54
54
|
bene_account_number: beneficiary_account,
|
|
55
55
|
source_account_number: source_account
|
|
56
56
|
)
|
|
57
|
-
|
|
58
|
-
puts
|
|
57
|
+
|
|
58
|
+
puts '✓ Account verified'
|
|
59
59
|
puts " Inquiry ID: #{inquiry_response['inquiry_id']}"
|
|
60
60
|
puts " Beneficiary Name: #{inquiry_response['account_name']}"
|
|
61
61
|
puts
|
|
62
62
|
|
|
63
63
|
# Step 2: Confirm transfer
|
|
64
|
-
print
|
|
64
|
+
print 'Proceed with transfer? (yes/no): '
|
|
65
65
|
confirmation = gets.chomp.downcase
|
|
66
|
-
|
|
67
|
-
unless
|
|
68
|
-
puts
|
|
66
|
+
|
|
67
|
+
unless %w[yes y].include?(confirmation)
|
|
68
|
+
puts 'Transfer cancelled'
|
|
69
69
|
return
|
|
70
70
|
end
|
|
71
71
|
puts
|
|
72
72
|
|
|
73
73
|
# Step 3: Execute transfer
|
|
74
|
-
puts
|
|
75
|
-
|
|
74
|
+
puts 'Step 2: Executing transfer...'
|
|
75
|
+
|
|
76
76
|
transfer_response = client.intra_transaction.fund_transfer(
|
|
77
|
-
request_id: DkPaymentGateway::Utils.generate_request_id(
|
|
77
|
+
request_id: DkPaymentGateway::Utils.generate_request_id('TXN'),
|
|
78
78
|
inquiry_id: inquiry_response['inquiry_id'],
|
|
79
|
-
transaction_datetime: Time.now.utc.strftime(
|
|
79
|
+
transaction_datetime: Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ'),
|
|
80
80
|
transaction_amount: amount,
|
|
81
|
-
currency:
|
|
82
|
-
payment_type:
|
|
81
|
+
currency: 'BTN',
|
|
82
|
+
payment_type: 'INTRA',
|
|
83
83
|
source_account_number: source_account,
|
|
84
84
|
bene_cust_name: inquiry_response['account_name'],
|
|
85
85
|
bene_account_number: beneficiary_account,
|
|
86
|
-
bene_bank_code:
|
|
87
|
-
narration:
|
|
86
|
+
bene_bank_code: '1060',
|
|
87
|
+
narration: 'Test transfer'
|
|
88
88
|
)
|
|
89
|
-
|
|
90
|
-
puts
|
|
89
|
+
|
|
90
|
+
puts '✓ Transfer completed successfully!'
|
|
91
91
|
puts " Transaction Status ID: #{transfer_response['txn_status_id']}"
|
|
92
92
|
puts
|
|
93
93
|
|
|
94
94
|
# Step 4: Verify transaction status
|
|
95
|
-
puts
|
|
96
|
-
|
|
95
|
+
puts 'Step 3: Verifying transaction status...'
|
|
96
|
+
|
|
97
97
|
# Wait a moment for transaction to process
|
|
98
98
|
sleep 2
|
|
99
|
-
|
|
100
|
-
#
|
|
99
|
+
|
|
100
|
+
# NOTE: You would use the actual transaction_id returned by the system
|
|
101
101
|
# This is just an example
|
|
102
|
-
puts
|
|
103
|
-
|
|
102
|
+
puts '✓ Transfer processed'
|
|
104
103
|
rescue DkPaymentGateway::TransactionError => e
|
|
105
104
|
puts "✗ Transaction error: #{e.message}"
|
|
106
105
|
puts " Error code: #{e.response_code}"
|
|
@@ -111,4 +110,3 @@ end
|
|
|
111
110
|
|
|
112
111
|
# Run the example
|
|
113
112
|
main if __FILE__ == $PROGRAM_NAME
|
|
114
|
-
|
|
@@ -12,7 +12,6 @@ module DkPaymentGateway
|
|
|
12
12
|
# Validates beneficiary account details before initiating a fund transfer
|
|
13
13
|
#
|
|
14
14
|
# @param params [Hash] Account inquiry parameters
|
|
15
|
-
# @option params [String] :request_id Unique identifier for the inquiry request
|
|
16
15
|
# @option params [Numeric] :amount Transaction amount
|
|
17
16
|
# @option params [String] :currency Currency code (e.g., "BTN")
|
|
18
17
|
# @option params [String] :bene_bank_code Beneficiary bank code (1060 for intra)
|
|
@@ -41,7 +40,6 @@ module DkPaymentGateway
|
|
|
41
40
|
# Initiates a fund transfer after successful account inquiry
|
|
42
41
|
#
|
|
43
42
|
# @param params [Hash] Fund transfer parameters
|
|
44
|
-
# @option params [String] :request_id Unique identifier for the request
|
|
45
43
|
# @option params [String] :inquiry_id Inquiry ID from account_inquiry
|
|
46
44
|
# @option params [String] :source_app Source application identifier
|
|
47
45
|
# @option params [Numeric] :transaction_amount Amount to transfer
|
|
@@ -76,12 +74,12 @@ module DkPaymentGateway
|
|
|
76
74
|
|
|
77
75
|
def build_inquiry_body(params)
|
|
78
76
|
{
|
|
79
|
-
request_id:
|
|
77
|
+
request_id: Utils.generate_request_id,
|
|
80
78
|
amount: params[:amount].to_s,
|
|
81
79
|
currency: params[:currency],
|
|
82
80
|
bene_bank_code: params[:bene_bank_code],
|
|
83
81
|
bene_account_number: params[:bene_account_number],
|
|
84
|
-
|
|
82
|
+
source_account_number: params[:source_account_number] # NOTE: API has typo "soure"
|
|
85
83
|
}.tap do |body|
|
|
86
84
|
body[:source_account_name] = params[:source_account_name] if params[:source_account_name]
|
|
87
85
|
end
|
|
@@ -89,7 +87,7 @@ module DkPaymentGateway
|
|
|
89
87
|
|
|
90
88
|
def build_transfer_body(params)
|
|
91
89
|
{
|
|
92
|
-
request_id:
|
|
90
|
+
request_id: Utils.generate_request_id,
|
|
93
91
|
inquiry_id: params[:inquiry_id],
|
|
94
92
|
transaction_datetime: params[:transaction_datetime],
|
|
95
93
|
source_app: params[:source_app] || client.config.source_app,
|
|
@@ -107,7 +105,7 @@ module DkPaymentGateway
|
|
|
107
105
|
end
|
|
108
106
|
|
|
109
107
|
def validate_inquiry_params!(params)
|
|
110
|
-
required = %i[
|
|
108
|
+
required = %i[amount currency bene_bank_code
|
|
111
109
|
bene_account_number source_account_number]
|
|
112
110
|
|
|
113
111
|
missing = required.select { |key| params[key].nil? || params[key].to_s.empty? }
|
|
@@ -116,7 +114,7 @@ module DkPaymentGateway
|
|
|
116
114
|
end
|
|
117
115
|
|
|
118
116
|
def validate_transfer_params!(params)
|
|
119
|
-
required = %i[
|
|
117
|
+
required = %i[inquiry_id transaction_amount currency
|
|
120
118
|
transaction_datetime bene_bank_code bene_account_number
|
|
121
119
|
bene_cust_name source_account_number narration]
|
|
122
120
|
|
|
@@ -140,7 +138,7 @@ module DkPaymentGateway
|
|
|
140
138
|
def generate_signature_headers(request_body)
|
|
141
139
|
raise SignatureError, 'Private key not available. Call client.authenticate! first' unless client.private_key
|
|
142
140
|
|
|
143
|
-
Signature.generate(client.private_key, request_body)
|
|
141
|
+
Signature.generate(client.private_key, client.access_token, request_body)
|
|
144
142
|
end
|
|
145
143
|
end
|
|
146
144
|
end
|
|
@@ -45,7 +45,6 @@ module DkPaymentGateway
|
|
|
45
45
|
# Completes a previously authorized payment by verifying OTP
|
|
46
46
|
#
|
|
47
47
|
# @param params [Hash] Debit request parameters
|
|
48
|
-
# @option params [String] :request_id Unique identifier for the request
|
|
49
48
|
# @option params [String] :bfs_txn_id Transaction ID from authorization
|
|
50
49
|
# @option params [String] :bfs_remitter_otp OTP sent to remitter
|
|
51
50
|
# @option params [String] :bfs_order_no Order number (optional)
|
|
@@ -107,7 +106,7 @@ module DkPaymentGateway
|
|
|
107
106
|
|
|
108
107
|
def build_debit_body(params)
|
|
109
108
|
{
|
|
110
|
-
request_id:
|
|
109
|
+
request_id: Utils.generate_request_id,
|
|
111
110
|
bfs_bfsTxnId: params[:bfs_txn_id],
|
|
112
111
|
bfs_remitter_Otp: params[:bfs_remitter_otp]
|
|
113
112
|
}.tap do |body|
|
|
@@ -126,7 +125,7 @@ module DkPaymentGateway
|
|
|
126
125
|
end
|
|
127
126
|
|
|
128
127
|
def validate_debit_params!(params)
|
|
129
|
-
required = %i[
|
|
128
|
+
required = %i[bfs_txn_id bfs_remitter_otp]
|
|
130
129
|
|
|
131
130
|
missing = required.select { |key| params[key].nil? || params[key].to_s.empty? }
|
|
132
131
|
|
|
@@ -148,7 +147,7 @@ module DkPaymentGateway
|
|
|
148
147
|
def generate_signature_headers(request_body)
|
|
149
148
|
raise SignatureError, 'Private key not available. Call client.authenticate! first' unless client.private_key
|
|
150
149
|
|
|
151
|
-
Signature.generate(client.private_key, request_body)
|
|
150
|
+
Signature.generate(client.private_key, client.access_token, request_body)
|
|
152
151
|
end
|
|
153
152
|
end
|
|
154
153
|
end
|
|
@@ -14,7 +14,6 @@ module DkPaymentGateway
|
|
|
14
14
|
# If amount > 0, generates a Dynamic QR (amount is fixed)
|
|
15
15
|
#
|
|
16
16
|
# @param params [Hash] QR generation parameters
|
|
17
|
-
# @option params [String] :request_id Unique identifier for the request
|
|
18
17
|
# @option params [String] :currency Currency code (e.g., "BTN")
|
|
19
18
|
# @option params [String] :bene_account_number Beneficiary account number
|
|
20
19
|
# @option params [Numeric] :amount Transaction amount (0 for static QR, >0 for dynamic QR)
|
|
@@ -54,18 +53,19 @@ module DkPaymentGateway
|
|
|
54
53
|
|
|
55
54
|
def build_qr_body(params)
|
|
56
55
|
{
|
|
57
|
-
request_id:
|
|
56
|
+
request_id: Utils.generate_request_id,
|
|
58
57
|
currency: params[:currency],
|
|
59
58
|
bene_account_number: params[:bene_account_number],
|
|
60
59
|
amount: params[:amount],
|
|
61
|
-
mcc_code: params[:mcc_code]
|
|
60
|
+
mcc_code: params[:mcc_code],
|
|
61
|
+
reference_no: params[:reference_no]
|
|
62
62
|
}.tap do |body|
|
|
63
63
|
body[:remarks] = params[:remarks] if params[:remarks]
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def validate_qr_params!(params)
|
|
68
|
-
required = %i[
|
|
68
|
+
required = %i[currency bene_account_number amount mcc_code]
|
|
69
69
|
|
|
70
70
|
missing = required.select { |key| params[key].nil? }
|
|
71
71
|
|
|
@@ -91,7 +91,7 @@ module DkPaymentGateway
|
|
|
91
91
|
def generate_signature_headers(request_body)
|
|
92
92
|
raise SignatureError, 'Private key not available. Call client.authenticate! first' unless client.private_key
|
|
93
93
|
|
|
94
|
-
Signature.generate(client.private_key, request_body)
|
|
94
|
+
Signature.generate(client.private_key, client.access_token, request_body)
|
|
95
95
|
end
|
|
96
96
|
end
|
|
97
97
|
end
|
|
@@ -8,10 +8,11 @@ require 'securerandom'
|
|
|
8
8
|
|
|
9
9
|
module DkPaymentGateway
|
|
10
10
|
class Signature
|
|
11
|
-
attr_reader :private_key
|
|
11
|
+
attr_reader :private_key, :access_token
|
|
12
12
|
|
|
13
|
-
def initialize(private_key)
|
|
13
|
+
def initialize(private_key, access_token)
|
|
14
14
|
@private_key = private_key
|
|
15
|
+
@access_token = access_token
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
# Generate signature headers for a request
|
|
@@ -24,7 +25,8 @@ module DkPaymentGateway
|
|
|
24
25
|
{
|
|
25
26
|
'DK-Signature' => "DKSignature #{signature}",
|
|
26
27
|
'DK-Timestamp' => timestamp,
|
|
27
|
-
'DK-Nonce' => nonce
|
|
28
|
+
'DK-Nonce' => nonce,
|
|
29
|
+
'Authorization' => "Bearer #{access_token}"
|
|
28
30
|
}
|
|
29
31
|
end
|
|
30
32
|
|
|
@@ -43,7 +45,8 @@ module DkPaymentGateway
|
|
|
43
45
|
# Sign the request using RS256 algorithm
|
|
44
46
|
def sign_request(request_body, timestamp, nonce)
|
|
45
47
|
# Serialize request body to canonical JSON (sorted keys, no spaces)
|
|
46
|
-
|
|
48
|
+
body_params = request_body.sort_by { |k, _| k }.to_h
|
|
49
|
+
request_body_str = JSON.generate(body_params, space: '', object_nl: '', array_nl: '')
|
|
47
50
|
|
|
48
51
|
# Base64 encode the request body
|
|
49
52
|
body_base64 = Base64.strict_encode64(request_body_str)
|
|
@@ -63,8 +66,8 @@ module DkPaymentGateway
|
|
|
63
66
|
|
|
64
67
|
class << self
|
|
65
68
|
# Convenience method to generate signature headers
|
|
66
|
-
def generate(private_key, request_body)
|
|
67
|
-
new(private_key).generate_headers(request_body)
|
|
69
|
+
def generate(private_key, access_token, request_body)
|
|
70
|
+
new(private_key, access_token).generate_headers(request_body)
|
|
68
71
|
end
|
|
69
72
|
end
|
|
70
73
|
end
|
|
@@ -12,12 +12,27 @@ module DkPaymentGateway
|
|
|
12
12
|
# Checks the status of a payment transaction for the current day
|
|
13
13
|
#
|
|
14
14
|
# @param params [Hash] Status check parameters
|
|
15
|
-
# @option params [String] :request_id Unique identifier for the request
|
|
16
15
|
# @option params [String] :transaction_id Transaction ID returned during payment
|
|
17
16
|
# @option params [String] :bene_account_number Beneficiary account number
|
|
18
17
|
#
|
|
19
18
|
# @return [Hash] Response containing transaction status details
|
|
20
|
-
def
|
|
19
|
+
def check_current_day_intra(params)
|
|
20
|
+
validate_current_day_params!(params)
|
|
21
|
+
|
|
22
|
+
request_body = build_current_day_body(params)
|
|
23
|
+
signature_headers = generate_signature_headers(request_body)
|
|
24
|
+
|
|
25
|
+
response = client.post(
|
|
26
|
+
'/v1/intra-transaction/status',
|
|
27
|
+
body: request_body.to_json,
|
|
28
|
+
headers: signature_headers
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
validate_response!(response, 'Transaction Status Check')
|
|
32
|
+
response['response_data']
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def check_current_day_inter(params)
|
|
21
36
|
validate_current_day_params!(params)
|
|
22
37
|
|
|
23
38
|
request_body = build_current_day_body(params)
|
|
@@ -25,7 +40,7 @@ module DkPaymentGateway
|
|
|
25
40
|
|
|
26
41
|
response = client.post(
|
|
27
42
|
'/v1/transaction/status',
|
|
28
|
-
body: request_body,
|
|
43
|
+
body: request_body.to_json,
|
|
29
44
|
headers: signature_headers
|
|
30
45
|
)
|
|
31
46
|
|
|
@@ -37,7 +52,6 @@ module DkPaymentGateway
|
|
|
37
52
|
# Checks the status of a payment transaction for previous business days
|
|
38
53
|
#
|
|
39
54
|
# @param params [Hash] Status check parameters
|
|
40
|
-
# @option params [String] :request_id Unique identifier for the request
|
|
41
55
|
# @option params [String] :transaction_id Transaction ID returned during payment
|
|
42
56
|
# @option params [String] :transaction_date Date when transaction was initiated (YYYY-MM-DD)
|
|
43
57
|
# @option params [String] :bene_account_number Beneficiary account number
|
|
@@ -51,7 +65,7 @@ module DkPaymentGateway
|
|
|
51
65
|
|
|
52
66
|
response = client.post(
|
|
53
67
|
'/v1/transactions/status',
|
|
54
|
-
body: request_body,
|
|
68
|
+
body: request_body.to_json,
|
|
55
69
|
headers: signature_headers
|
|
56
70
|
)
|
|
57
71
|
|
|
@@ -67,15 +81,15 @@ module DkPaymentGateway
|
|
|
67
81
|
|
|
68
82
|
def build_current_day_body(params)
|
|
69
83
|
{
|
|
70
|
-
request_id:
|
|
71
|
-
|
|
84
|
+
request_id: Utils.generate_request_id,
|
|
85
|
+
reference_no: params[:reference_no],
|
|
72
86
|
bene_account_number: params[:bene_account_number]
|
|
73
87
|
}
|
|
74
88
|
end
|
|
75
89
|
|
|
76
90
|
def build_previous_days_body(params)
|
|
77
91
|
{
|
|
78
|
-
request_id:
|
|
92
|
+
request_id: Utils.generate_request_id,
|
|
79
93
|
transaction_id: params[:transaction_id],
|
|
80
94
|
trasnaction_date: params[:transaction_date], # NOTE: API has typo "trasnaction"
|
|
81
95
|
bene_account_number: params[:bene_account_number]
|
|
@@ -83,7 +97,7 @@ module DkPaymentGateway
|
|
|
83
97
|
end
|
|
84
98
|
|
|
85
99
|
def validate_current_day_params!(params)
|
|
86
|
-
required = %i[
|
|
100
|
+
required = %i[reference_no bene_account_number]
|
|
87
101
|
|
|
88
102
|
missing = required.select { |key| params[key].nil? || params[key].to_s.empty? }
|
|
89
103
|
|
|
@@ -91,7 +105,7 @@ module DkPaymentGateway
|
|
|
91
105
|
end
|
|
92
106
|
|
|
93
107
|
def validate_previous_days_params!(params)
|
|
94
|
-
required = %i[
|
|
108
|
+
required = %i[transaction_id transaction_date bene_account_number]
|
|
95
109
|
|
|
96
110
|
missing = required.select { |key| params[key].nil? || params[key].to_s.empty? }
|
|
97
111
|
|
|
@@ -120,7 +134,7 @@ module DkPaymentGateway
|
|
|
120
134
|
def generate_signature_headers(request_body)
|
|
121
135
|
raise SignatureError, 'Private key not available. Call client.authenticate! first' unless client.private_key
|
|
122
136
|
|
|
123
|
-
Signature.generate(client.private_key, request_body)
|
|
137
|
+
Signature.generate(client.private_key, client.access_token, request_body)
|
|
124
138
|
end
|
|
125
139
|
end
|
|
126
140
|
end
|
data/simple_test.rb
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Simple test focusing only on the fixed class
|
|
4
|
+
|
|
5
|
+
class MockClient
|
|
6
|
+
def initialize
|
|
7
|
+
@private_key = 'test_key'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
attr_reader :private_key
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class MockSignature
|
|
14
|
+
def self.generate(private_key, request_body)
|
|
15
|
+
{}
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Manually define the IntraTransaction class with our fix
|
|
20
|
+
module DkPaymentGateway
|
|
21
|
+
class IntraTransaction
|
|
22
|
+
def initialize(client)
|
|
23
|
+
@client = client
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def build_inquiry_body(params)
|
|
27
|
+
{
|
|
28
|
+
request_id: params[:request_id],
|
|
29
|
+
amount: params[:amount].to_s,
|
|
30
|
+
currency: params[:currency],
|
|
31
|
+
bene_bank_code: params[:bene_bank_code],
|
|
32
|
+
bene_account_number: params[:bene_account_number],
|
|
33
|
+
source_account_number: params[:source_account_number] # This is the fixed line
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def build_transfer_body(params)
|
|
38
|
+
{
|
|
39
|
+
request_id: params[:request_id],
|
|
40
|
+
amount: params[:amount].to_s,
|
|
41
|
+
currency: params[:currency],
|
|
42
|
+
source_account_number: params[:source_account_number],
|
|
43
|
+
destination_account_number: params[:destination_account_number],
|
|
44
|
+
destination_bank_code: params[:destination_bank_code]
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
begin
|
|
51
|
+
puts 'Testing the fixed IntraTransaction class...'
|
|
52
|
+
|
|
53
|
+
client = MockClient.new
|
|
54
|
+
intra = DkPaymentGateway::IntraTransaction.new(client)
|
|
55
|
+
|
|
56
|
+
params = {
|
|
57
|
+
request_id: 'test123',
|
|
58
|
+
amount: '1000',
|
|
59
|
+
currency: 'ETB',
|
|
60
|
+
bene_bank_code: '123456',
|
|
61
|
+
bene_account_number: '987654321',
|
|
62
|
+
source_account_number: '123456789'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# Test that the method works without error
|
|
66
|
+
request_body = intra.build_inquiry_body(params)
|
|
67
|
+
puts '✓ build_inquiry_body executed successfully!'
|
|
68
|
+
|
|
69
|
+
# Verify the parameter was correctly accessed
|
|
70
|
+
if request_body[:source_account_number] == '123456789'
|
|
71
|
+
puts '✓ source_account_number parameter correctly accessed!'
|
|
72
|
+
else
|
|
73
|
+
puts '❌ source_account_number parameter not correctly accessed'
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
puts "\n🎉 Test passed - the typo fix is working correctly!"
|
|
77
|
+
rescue StandardError => e
|
|
78
|
+
puts "❌ Error: #{e.message}"
|
|
79
|
+
puts e.backtrace
|
|
80
|
+
end
|
data/verify_fix.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Simple test to verify the fix works
|
|
4
|
+
begin
|
|
5
|
+
# Add the lib directory to the load path
|
|
6
|
+
$LOAD_PATH.unshift 'lib'
|
|
7
|
+
|
|
8
|
+
# Try to require the main module
|
|
9
|
+
require 'dk_payment_gateway'
|
|
10
|
+
|
|
11
|
+
puts "✓ Module loaded successfully!"
|
|
12
|
+
|
|
13
|
+
# Test that we can instantiate the client
|
|
14
|
+
client = DkPaymentGateway::Client.new(
|
|
15
|
+
public_key: 'test_key',
|
|
16
|
+
private_key: 'test_key'
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
puts "✓ Client instantiated successfully!"
|
|
20
|
+
|
|
21
|
+
# Test that we can access the intra transaction functionality
|
|
22
|
+
intra = DkPaymentGateway::IntraTransaction.new(client)
|
|
23
|
+
puts "✓ IntraTransaction class instantiated successfully!"
|
|
24
|
+
|
|
25
|
+
# Test that the method exists and can be called
|
|
26
|
+
params = {
|
|
27
|
+
request_id: 'test123',
|
|
28
|
+
amount: '1000',
|
|
29
|
+
currency: 'ETB',
|
|
30
|
+
bene_bank_code: '123456',
|
|
31
|
+
bene_account_number: '987654321',
|
|
32
|
+
source_account_number: '123456789'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# This should work now without the NoMethodError
|
|
36
|
+
request_body = intra.send(:build_inquiry_body, params)
|
|
37
|
+
puts "✓ build_inquiry_body method works correctly!"
|
|
38
|
+
puts "✓ Request body generated: #{request_body.inspect}"
|
|
39
|
+
|
|
40
|
+
puts "\n🎉 All tests passed - the fix is working correctly!"
|
|
41
|
+
|
|
42
|
+
rescue => e
|
|
43
|
+
puts "❌ Error: #{e.message}"
|
|
44
|
+
puts e.backtrace
|
|
45
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dk_payment_gateway
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tashi Dendup
|
|
@@ -131,6 +131,7 @@ files:
|
|
|
131
131
|
- README.md
|
|
132
132
|
- Rakefile
|
|
133
133
|
- SUMMARY.md
|
|
134
|
+
- TESTING.md
|
|
134
135
|
- dk_payment_gateway.gemspec
|
|
135
136
|
- examples/README.md
|
|
136
137
|
- examples/generate_qr.rb
|
|
@@ -148,6 +149,8 @@ files:
|
|
|
148
149
|
- lib/dk_payment_gateway/transaction_status.rb
|
|
149
150
|
- lib/dk_payment_gateway/utils.rb
|
|
150
151
|
- lib/dk_payment_gateway/version.rb
|
|
152
|
+
- simple_test.rb
|
|
153
|
+
- verify_fix.rb
|
|
151
154
|
homepage: https://github.com/dcplbt/dk_payment_gateway
|
|
152
155
|
licenses:
|
|
153
156
|
- MIT
|
|
@@ -169,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
169
172
|
- !ruby/object:Gem::Version
|
|
170
173
|
version: '0'
|
|
171
174
|
requirements: []
|
|
172
|
-
rubygems_version:
|
|
175
|
+
rubygems_version: 4.0.3
|
|
173
176
|
specification_version: 4
|
|
174
177
|
summary: Ruby client for DK Payment Gateway API
|
|
175
178
|
test_files: []
|