razorpay 3.2.1 → 3.2.2
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/CHANGELOG.md +7 -0
- data/README.md +16 -1
- data/documents/oauth_token.md +142 -0
- data/lib/razorpay/constants.rb +6 -1
- data/lib/razorpay/oauth_token.rb +109 -0
- data/lib/razorpay/payload_validator.rb +93 -0
- data/lib/razorpay/request.rb +24 -7
- data/lib/razorpay/utility.rb +24 -0
- data/lib/razorpay/validation_config.rb +11 -0
- data/lib/razorpay.rb +8 -1
- data/test/fixtures/fake_oauth_token.json +8 -0
- data/test/fixtures/fake_revoke_token.json +3 -0
- data/test/razorpay/test_oauth_token.rb +105 -0
- data/test/razorpay/test_payload_validator.rb +61 -0
- data/test/razorpay/test_razorpay.rb +17 -0
- data/test/razorpay/test_utility.rb +34 -0
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f1899756cb2c34271a9cd8167029bb2aa56d3a07094df776feab8de024aa69b
|
4
|
+
data.tar.gz: daf3f8a28f3d5f211a1f83de9772d4f71fa3aa8b0b415bb0731846930584e11a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9458dcd7356a9017800f5ec4cdae747f17e4595ab7b9f194cb8f14c617f41b8c077497b4438afbd559ecd2f6c0f580f4de5ca5935ed7d7aa6de05679e27be535
|
7
|
+
data.tar.gz: 3377a46637db6e97d2c2435818de9d8b4782aac20d72adf4721e149c9fff2b7fbe41a83a0374090d2556cec2b996438160850d95ffa3b7f0d92a6f99d22aa024
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,13 @@ Changelog for Razorpay-Ruby SDK.
|
|
4
4
|
|
5
5
|
## Unreleased
|
6
6
|
|
7
|
+
## [3.2.2] - 2024-04-16
|
8
|
+
|
9
|
+
feat: Added oauth APIs and support for access token based authentication mechanism
|
10
|
+
* Added oauth APIs (getAuthURL, getAccessToken, refreshToken, revokeToken)
|
11
|
+
* Added support for access token based authentication mechanism
|
12
|
+
* Added support for onboarding signature generation
|
13
|
+
|
7
14
|
## [3.2.1] - 2023-12-19
|
8
15
|
|
9
16
|
Rollback: Generic access point due to some performance concern
|
data/README.md
CHANGED
@@ -27,7 +27,11 @@ Ruby 2.6.8 or later
|
|
27
27
|
|
28
28
|
Remember to `require 'razorpay'` before anything else.
|
29
29
|
|
30
|
-
Next, you need to setup your
|
30
|
+
Next, you need to setup your auth details. This setup can be done via two ways:
|
31
|
+
|
32
|
+
### Using Private Auth
|
33
|
+
|
34
|
+
you need to setup your key and secret using the following:
|
31
35
|
|
32
36
|
```rb
|
33
37
|
Razorpay.setup('key_id', 'key_secret')
|
@@ -38,6 +42,16 @@ You can set customer headers for your requests using the following:
|
|
38
42
|
Razorpay.headers = {"CUSTOM_APP_HEADER" => "CUSTOM_VALUE"}
|
39
43
|
```
|
40
44
|
|
45
|
+
### Using Access Token
|
46
|
+
you need to setup your access token using the following
|
47
|
+
```rb
|
48
|
+
Razorpay.setup_with_oauth('access_token')
|
49
|
+
```
|
50
|
+
You can set customer headers for your requests using the following:
|
51
|
+
```rb
|
52
|
+
Razorpay.headers = {"CUSTOM_APP_HEADER" => "CUSTOM_VALUE"}
|
53
|
+
```
|
54
|
+
|
41
55
|
You can find your API keys at <https://dashboard.razorpay.com/#/app/keys>.
|
42
56
|
|
43
57
|
If you are using rails, the right place to do this might be `config/initializers/razorpay.rb`.
|
@@ -70,6 +84,7 @@ If you are using rails, the right place to do this might be `config/initializers
|
|
70
84
|
- [Register NACH and Charge First Payment Together](documents/registerNach.md)
|
71
85
|
- [Payment Verification](documents/paymentVerification.md)
|
72
86
|
- [Webhook](documents/webhook.md)
|
87
|
+
- [OAuthToken](documents/oauth_token.md)
|
73
88
|
|
74
89
|
## Development
|
75
90
|
|
@@ -0,0 +1,142 @@
|
|
1
|
+
### OAuthToken
|
2
|
+
|
3
|
+
```rb
|
4
|
+
require "razorpay"
|
5
|
+
```
|
6
|
+
|
7
|
+
### Generate Authorize Url
|
8
|
+
```rb
|
9
|
+
body = {
|
10
|
+
submerchant_id: '<SUBMERCHANT_MID>',
|
11
|
+
timestamp: Time.now.to_i
|
12
|
+
}
|
13
|
+
onboarding_signature = Razorpay::Utility.generate_onboarding_signature(body, '<YOUR_CLIENT_SECRET>')
|
14
|
+
|
15
|
+
options = {
|
16
|
+
'client_id' => '<YOUR_CLIENT_ID>',
|
17
|
+
'redirect_uri' => 'https://example.com/razorpay_callback',
|
18
|
+
'scopes' => ["read_write"],
|
19
|
+
'state' => 'NOBYtv8r6c75ex6WZ',
|
20
|
+
'onboarding_signature' => onboarding_signature
|
21
|
+
}
|
22
|
+
authorize_url = Razorpay::OAuthToken.get_auth_url(options)
|
23
|
+
```
|
24
|
+
|
25
|
+
**Parameters:**
|
26
|
+
|
27
|
+
| Name | Type | Description |
|
28
|
+
|----------------------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
|
29
|
+
| client_id* | string | Unique client identifier. |
|
30
|
+
| redirect_uri* | string | Callback URL used by Razorpay to redirect after the user approves or denies the authorisation request. The client should whitelist the 'redirect_uri'. |
|
31
|
+
| scopes* | array | Defines what access your application is requesting from the user. You can request one or multiple scopes by adding them to an array as indicated above. |
|
32
|
+
| state* | string | A random string generated by your service. This parameter helps prevent cross-site request forgery (CSRF) attacks. |
|
33
|
+
| onboarding_signature | string | A cryptographic string generated by your service using generateOnboardingSignature method in Utils class. Only applicable for accounts created with pre-fill KYC |
|
34
|
+
|
35
|
+
**Response:**
|
36
|
+
```
|
37
|
+
"https://auth.razorpay.com/authorize?response_type=code&client_id=<YOUR_CLIENT_ID>&redirect_uri=https:%2F%2Fexample.com%2Frazorpay_callback&scope[]=read_only&scope[]=rx_read_write&state=NOBYtv8r6c75ex6WZ&onboarding_signature=<GENERATED_ONBOARDING_SIGNATURE>"
|
38
|
+
```
|
39
|
+
|
40
|
+
-------------------------------------------------------------------------------------------------------
|
41
|
+
### Get Access token
|
42
|
+
```rb
|
43
|
+
options = {
|
44
|
+
'client_id' => '<YOUR_CLIENT_ID>',
|
45
|
+
'client_secret' => '<YOUR_CLIENT_SECRET>',
|
46
|
+
'redirect_uri' => 'https://example.com/razorpay_callback',
|
47
|
+
'grant_type' => 'authorization_code',
|
48
|
+
'code' => '<AUTHORIZATION_CODE>',
|
49
|
+
'mode' => 'test'
|
50
|
+
}
|
51
|
+
oauth_token = Razorpay::OAuthToken.get_access_token(options)
|
52
|
+
```
|
53
|
+
|
54
|
+
**Parameters:**
|
55
|
+
|
56
|
+
| Name | Type | Description |
|
57
|
+
|----------------|--------|------------------------------------------------------------------------------------------------------------------------------|
|
58
|
+
| client_id* | string | Unique client identifier. |
|
59
|
+
| client_secret* | string | Client secret string. |
|
60
|
+
| redirect_uri* | string | Specifies the same redirect_uri used in the authorisation request. |
|
61
|
+
| grant_type* | string | Defines the grant type for the request. Possible value are:<ul><li>authorization_code</li><li>client_credentials</li></ul> |
|
62
|
+
| code* | string | Decoded authorisation code received in the last step. Note: Pass this parameter only when grant_type is 'authorization_code' |
|
63
|
+
| mode | string | The type of mode. Possible values: <ul><li>test</li><li>live (default)</li></ul> |
|
64
|
+
|
65
|
+
**Response:**
|
66
|
+
```json
|
67
|
+
{
|
68
|
+
"public_token": "rzp_test_oauth_9xu1rkZqoXlClS",
|
69
|
+
"token_type": "Bearer",
|
70
|
+
"expires_in": 7862400,
|
71
|
+
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IkY1Z0NQYkhhRzRjcUpnIn0.eyJhdWQiOiJGNFNNeEgxanMxbkpPZiIsImp0aSI6IkY1Z0NQYkhhRzRjcUpnIiwiaWF0IjoxNTkyODMxMDExLCJuYmYiOjE1OTI4MzEwMTEsInN1YiI6IiIsImV4cCI6MTYwMDc3OTgxMSwidXNlcl9pZCI6IkYycVBpejJEdzRPRVFwIiwibWVyY2hhbnRfaWQiOiJGMnFQaVZ3N0lNV01GSyIsInNjb3BlcyI6WyJyZWFkX29ubHkiXX0.Wwqt5czhoWpVzP5_aoiymKXoGj-ydo-4A_X2jf_7rrSvk4pXdqzbA5BMrHxPdPbeFQWV6vsnsgbf99Q3g-W4kalHyH67LfAzc3qnJ-mkYDkFY93tkeG-MCco6GJW-Jm8xhaV9EPUak7z9J9jcdluu9rNXYMtd5qxD8auyRYhEgs",
|
72
|
+
"refresh_token": "def50200f42e07aded65a323f6c53181d802cc797b62cc5e78dd8038d6dff253e5877da9ad32f463a4da0ad895e3de298cbce40e162202170e763754122a6cb97910a1f58e2378ee3492dc295e1525009cccc45635308cce8575bdf373606c453ebb5eb2bec062ca197ac23810cf9d6cf31fbb9fcf5b7d4de9bf524c89a4aa90599b0151c9e4e2fa08acb6d2fe17f30a6cfecdfd671f090787e821f844e5d36f5eacb7dfb33d91e83b18216ad0ebeba2bef7721e10d436c3984daafd8654ed881c581d6be0bdc9ebfaee0dc5f9374d7184d60aae5aa85385690220690e21bc93209fb8a8cc25a6abf1108d8277f7c3d38217b47744d7",
|
73
|
+
"razorpay_account_id": "acc_Dhk2qDbmu6FwZH"
|
74
|
+
}
|
75
|
+
```
|
76
|
+
|
77
|
+
-------------------------------------------------------------------------------------------------------
|
78
|
+
|
79
|
+
### Get Access token using refresh token
|
80
|
+
```rb
|
81
|
+
options = {
|
82
|
+
'client_id' => '<YOUR_CLIENT_ID>',
|
83
|
+
'client_secret' => '<YOUR_CLIENT_SECRET>',
|
84
|
+
'refresh_token' => 'def5020096e1c470c901d34cd60fa53abdaf3662sa0'
|
85
|
+
}
|
86
|
+
oauth_token = Razorpay::OAuthToken.refresh_token(options)
|
87
|
+
```
|
88
|
+
|
89
|
+
**Parameters:**
|
90
|
+
|
91
|
+
| Name | Type | Description |
|
92
|
+
|----------------|-----------|--------------------------------------------|
|
93
|
+
| client_id* | string | Unique client identifier. |
|
94
|
+
| client_secret* | string | Client secret string. |
|
95
|
+
| refresh_token* | string | The previously-stored refresh token value. |
|
96
|
+
|
97
|
+
**Response:**
|
98
|
+
```json
|
99
|
+
{
|
100
|
+
"public_token": "rzp_test_oauth_9xu1rkZqoXlClS",
|
101
|
+
"token_type": "Bearer",
|
102
|
+
"expires_in": 7862400,
|
103
|
+
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6Ijl4dTF",
|
104
|
+
"refresh_token": "def5020096e1c470c901d34cd60fa53abdaf36620e823ffa53"
|
105
|
+
}
|
106
|
+
```
|
107
|
+
|
108
|
+
-------------------------------------------------------------------------------------------------------
|
109
|
+
|
110
|
+
### Revoke a token
|
111
|
+
```rb
|
112
|
+
options = {
|
113
|
+
'client_id' => '<YOUR_CLIENT_ID>',
|
114
|
+
'client_secret' => '<YOUR_CLIENT_SECRET>',
|
115
|
+
'token' => 'def5020096e1c470c901d34cd60fa53abdaf36620e823ffa53'
|
116
|
+
'token_type_hint' => 'access_token'
|
117
|
+
}
|
118
|
+
response = Razorpay::OAuthToken.revoke_token(options)
|
119
|
+
```
|
120
|
+
|
121
|
+
**Parameters:**
|
122
|
+
|
123
|
+
| Name | Type | Description |
|
124
|
+
|------------------|----------|----------------------------------------------------------------------------------------------------------|
|
125
|
+
| client_id* | string | Unique client identifier. |
|
126
|
+
| client_secret* | string | Client secret string. |
|
127
|
+
| token_type_hint* | string | The type of token for the request. Possible values: <ul><li>access_token</li><li>refresh_token</li></ul> |
|
128
|
+
| token* | string | The token whose access should be revoked. |
|
129
|
+
|
130
|
+
**Response:**
|
131
|
+
```json
|
132
|
+
{
|
133
|
+
"message": "Token Revoked"
|
134
|
+
}
|
135
|
+
```
|
136
|
+
-------------------------------------------------------------------------------------------------------
|
137
|
+
|
138
|
+
**PN: * indicates mandatory fields**
|
139
|
+
<br>
|
140
|
+
<br>
|
141
|
+
**For reference click [here](https://razorpay.com/docs/partners/platform/onboard-businesses/integrate-oauth/integration-steps)**
|
142
|
+
|
data/lib/razorpay/constants.rb
CHANGED
@@ -2,5 +2,10 @@
|
|
2
2
|
module Razorpay
|
3
3
|
BASE_URI = 'https://api.razorpay.com'.freeze
|
4
4
|
TEST_URL = 'https://api.razorpay.com/'.freeze
|
5
|
-
VERSION
|
5
|
+
VERSION = '3.2.2'.freeze
|
6
|
+
AUTH_URL = 'https://auth.razorpay.com'.freeze
|
7
|
+
API_HOST = 'API'.freeze
|
8
|
+
AUTH_HOST = 'AUTH'.freeze
|
9
|
+
PRIVATE_AUTH = 'Private'.freeze
|
10
|
+
OAUTH = 'OAuth'.freeze
|
6
11
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'razorpay/request'
|
2
|
+
require 'razorpay/entity'
|
3
|
+
require 'razorpay/payload_validator'
|
4
|
+
require 'razorpay/validation_config'
|
5
|
+
|
6
|
+
module Razorpay
|
7
|
+
# OAuth APIs allow to you create and manage access tokens
|
8
|
+
class OAuthToken < Entity
|
9
|
+
def self.request
|
10
|
+
Razorpay::Request.new('token', Razorpay::AUTH_HOST)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.get_auth_url(options)
|
14
|
+
validate_auth_url_request(options)
|
15
|
+
uri = URI.join(Razorpay::AUTH_URL, '/authorize')
|
16
|
+
|
17
|
+
query_params = {
|
18
|
+
'response_type' => 'code',
|
19
|
+
'client_id' => options['client_id'],
|
20
|
+
'redirect_uri' => options['redirect_uri'],
|
21
|
+
'state' => options['state']
|
22
|
+
}
|
23
|
+
|
24
|
+
options['scopes'].each { |scope| query_params["scope[]"] = scope }
|
25
|
+
|
26
|
+
if options.has_key?('onboarding_signature')
|
27
|
+
query_params['onboarding_signature'] = options['onboarding_signature']
|
28
|
+
end
|
29
|
+
uri.query = URI.encode_www_form(query_params)
|
30
|
+
uri.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.get_access_token(options)
|
34
|
+
validate_access_token_request(options)
|
35
|
+
r = request
|
36
|
+
r.request :post, "/token", options
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.refresh_token(options)
|
40
|
+
options['grant_type'] = 'refresh_token'
|
41
|
+
validate_refresh_token_request(options)
|
42
|
+
r = request
|
43
|
+
r.request :post, "/token", options
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.revoke_token(options)
|
47
|
+
validate_revoke_token_request(options)
|
48
|
+
r = request
|
49
|
+
r.request :post, "/revoke", options
|
50
|
+
end
|
51
|
+
|
52
|
+
class << self
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def validate_auth_url_request(options)
|
57
|
+
Razorpay::PayloadValidator.validate(options, get_validations_for_auth_request_url)
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate_access_token_request(options)
|
61
|
+
Razorpay::PayloadValidator.validate(options, get_validations_for_access_token_request)
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_refresh_token_request(options)
|
65
|
+
Razorpay::PayloadValidator.validate(options, get_validations_for_refresh_token_request)
|
66
|
+
end
|
67
|
+
|
68
|
+
def validate_revoke_token_request(options)
|
69
|
+
Razorpay::PayloadValidator.validate(options, get_validations_for_revoke_token_request)
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_validations_for_auth_request_url
|
73
|
+
[
|
74
|
+
Razorpay::ValidationConfig.new('client_id', [:id]),
|
75
|
+
Razorpay::ValidationConfig.new('redirect_uri', [:non_empty_string, :url]),
|
76
|
+
Razorpay::ValidationConfig.new('scopes', [:non_null]),
|
77
|
+
Razorpay::ValidationConfig.new('state', [:non_empty_string])
|
78
|
+
]
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_validations_for_access_token_request
|
82
|
+
[
|
83
|
+
Razorpay::ValidationConfig.new('client_id', [:id]),
|
84
|
+
Razorpay::ValidationConfig.new('client_secret', [:non_empty_string]),
|
85
|
+
Razorpay::ValidationConfig.new('redirect_uri', [:non_empty_string, :url]),
|
86
|
+
Razorpay::ValidationConfig.new('grant_type', [:token_grant])
|
87
|
+
]
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_validations_for_refresh_token_request
|
91
|
+
[
|
92
|
+
Razorpay::ValidationConfig.new('client_id', [:id]),
|
93
|
+
Razorpay::ValidationConfig.new('client_secret', [:non_empty_string]),
|
94
|
+
Razorpay::ValidationConfig.new('refresh_token', [:non_empty_string]),
|
95
|
+
Razorpay::ValidationConfig.new('grant_type', [:token_grant])
|
96
|
+
]
|
97
|
+
end
|
98
|
+
|
99
|
+
def get_validations_for_revoke_token_request
|
100
|
+
[
|
101
|
+
Razorpay::ValidationConfig.new('client_id', [:id]),
|
102
|
+
Razorpay::ValidationConfig.new('client_secret', [:non_empty_string]),
|
103
|
+
Razorpay::ValidationConfig.new('token', [:non_empty_string]),
|
104
|
+
Razorpay::ValidationConfig.new('token_type_hint', [:non_empty_string])
|
105
|
+
]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Razorpay
|
2
|
+
|
3
|
+
ValidationType = {
|
4
|
+
non_null: :non_null,
|
5
|
+
non_empty_string: :non_empty_string,
|
6
|
+
url: :url,
|
7
|
+
id: :id,
|
8
|
+
mode: :mode,
|
9
|
+
token_grant: :token_grant
|
10
|
+
}
|
11
|
+
|
12
|
+
# PayloadValidator allows to perform basic validations
|
13
|
+
class PayloadValidator
|
14
|
+
def self.validate(request, validation_configs)
|
15
|
+
validation_configs.each do |config|
|
16
|
+
field_name = config.field_name
|
17
|
+
config.validations.each do |validation_type|
|
18
|
+
apply_validation(request, field_name, validation_type)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class << self
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def apply_validation(payload, field, validation_type)
|
28
|
+
case validation_type
|
29
|
+
when ValidationType[:non_null]
|
30
|
+
validate_non_null(payload, field)
|
31
|
+
when ValidationType[:non_empty_string]
|
32
|
+
validate_non_empty_string(payload, field)
|
33
|
+
when ValidationType[:url]
|
34
|
+
validate_url(payload, field)
|
35
|
+
when ValidationType[:id]
|
36
|
+
validate_id(payload, field)
|
37
|
+
when ValidationType[:mode]
|
38
|
+
validate_mode(payload, field)
|
39
|
+
when ValidationType[:token_grant]
|
40
|
+
validate_grant_type(payload, field)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_non_null(payload, field)
|
45
|
+
raise Razorpay::Error.new, "Field #{field} cannot be null" unless payload.key?(field) && !payload[field].nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
def validate_non_empty_string(payload, field)
|
49
|
+
raise Razorpay::Error.new, "Field #{field} cannot be empty" if payload[field].to_s.strip.empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
def validate_url(payload, field)
|
53
|
+
url = payload[field]
|
54
|
+
url_regex = /^(http[s]?):\/\/[^\s\/$.?#].[^\s]*$/
|
55
|
+
|
56
|
+
unless url_regex.match?(url)
|
57
|
+
error_message = "Field #{field} is not a valid URL"
|
58
|
+
raise Razorpay::Error.new, error_message
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def validate_id(payload, field)
|
63
|
+
validate_non_null(payload, field)
|
64
|
+
validate_non_empty_string(payload, field)
|
65
|
+
value = payload[field]
|
66
|
+
id_regex = /^[A-Za-z0-9]{1,14}$/
|
67
|
+
|
68
|
+
unless value.match?(id_regex)
|
69
|
+
error_message = "Field #{field} is not a valid ID"
|
70
|
+
raise Razorpay::Error.new, error_message
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def validate_mode(payload, field)
|
75
|
+
validate_non_null(payload, field)
|
76
|
+
unless ["test", "live"].include?(payload[field])
|
77
|
+
error_message = "Invalid value provided for field #{field}"
|
78
|
+
raise Razorpay::Error.new, error_message
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def validate_grant_type(payload, field)
|
83
|
+
validate_non_null(payload, field)
|
84
|
+
case payload[field]
|
85
|
+
when 'authorization_code'
|
86
|
+
validate_non_null(payload, 'code');
|
87
|
+
when 'refresh_token'
|
88
|
+
validate_non_null(payload, 'refresh_token');
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/razorpay/request.rb
CHANGED
@@ -11,20 +11,30 @@ module Razorpay
|
|
11
11
|
|
12
12
|
ssl_ca_file File.dirname(__FILE__) + '/../ca-bundle.crt'
|
13
13
|
|
14
|
-
def initialize(entity_name = nil)
|
15
|
-
self.class.base_uri(
|
14
|
+
def initialize(entity_name = nil, host = Razorpay::API_HOST)
|
15
|
+
self.class.base_uri(get_base_url(host))
|
16
16
|
@entity_name = entity_name
|
17
17
|
custom_headers = Razorpay.custom_headers || {}
|
18
18
|
predefined_headers = {
|
19
19
|
'User-Agent' => "Razorpay-Ruby/#{Razorpay::VERSION}; Ruby/#{RUBY_VERSION}"
|
20
20
|
}
|
21
|
+
|
21
22
|
# Order is important to give precedence to predefined headers
|
22
23
|
headers = custom_headers.merge(predefined_headers)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
|
25
|
+
if Razorpay.auth_type == Razorpay::OAUTH
|
26
|
+
@options = {
|
27
|
+
timeout: 30,
|
28
|
+
headers: headers
|
29
|
+
}
|
30
|
+
headers['Authorization'] = 'Bearer ' + Razorpay.access_token
|
31
|
+
else
|
32
|
+
@options = {
|
33
|
+
basic_auth: Razorpay.auth,
|
34
|
+
timeout: 30,
|
35
|
+
headers: headers
|
36
|
+
}
|
37
|
+
end
|
28
38
|
end
|
29
39
|
|
30
40
|
def fetch(id, version="v1")
|
@@ -74,6 +84,13 @@ module Razorpay
|
|
74
84
|
self.class.send(method, url, @options)
|
75
85
|
end
|
76
86
|
|
87
|
+
def get_base_url(host)
|
88
|
+
if host == Razorpay::AUTH_HOST
|
89
|
+
return Razorpay::AUTH_URL
|
90
|
+
end
|
91
|
+
Razorpay::BASE_URI
|
92
|
+
end
|
93
|
+
|
77
94
|
# Since we need to change the base route
|
78
95
|
def make_test_request
|
79
96
|
self.class.get Razorpay::TEST_URL, @options
|
data/lib/razorpay/utility.rb
CHANGED
@@ -26,6 +26,11 @@ module Razorpay
|
|
26
26
|
verify_signature(body, signature, secret)
|
27
27
|
end
|
28
28
|
|
29
|
+
def self.generate_onboarding_signature(body, secret)
|
30
|
+
json_data = body.to_json
|
31
|
+
encrypt(json_data, secret);
|
32
|
+
end
|
33
|
+
|
29
34
|
class << self
|
30
35
|
private
|
31
36
|
|
@@ -52,6 +57,25 @@ module Razorpay
|
|
52
57
|
|
53
58
|
r.zero?
|
54
59
|
end
|
60
|
+
|
61
|
+
def encrypt(data, secret)
|
62
|
+
iv = secret[0, 12]
|
63
|
+
key = secret[0, 16]
|
64
|
+
|
65
|
+
cipher = OpenSSL::Cipher.new('aes-128-gcm')
|
66
|
+
cipher.encrypt
|
67
|
+
cipher.key = key
|
68
|
+
cipher.iv = iv
|
69
|
+
|
70
|
+
cipher.auth_data = ""
|
71
|
+
|
72
|
+
encrypted = cipher.update(data) + cipher.final
|
73
|
+
|
74
|
+
tag = cipher.auth_tag
|
75
|
+
combined_encrypted_data = encrypted + tag
|
76
|
+
|
77
|
+
encrypted_data_hex = combined_encrypted_data.unpack1("H*")
|
78
|
+
end
|
55
79
|
end
|
56
80
|
end
|
57
81
|
end
|
data/lib/razorpay.rb
CHANGED
@@ -26,15 +26,22 @@ require 'razorpay/token'
|
|
26
26
|
require 'razorpay/product'
|
27
27
|
require 'razorpay/stakeholder'
|
28
28
|
require 'razorpay/account'
|
29
|
+
require 'razorpay/oauth_token'
|
29
30
|
|
30
31
|
# Base Razorpay module
|
31
32
|
module Razorpay
|
32
33
|
class << self
|
33
|
-
attr_accessor :auth, :custom_headers
|
34
|
+
attr_accessor :auth, :custom_headers, :access_token, :auth_type
|
34
35
|
end
|
35
36
|
|
36
37
|
def self.setup(key_id, key_secret)
|
37
38
|
self.auth = { username: key_id, password: key_secret }
|
39
|
+
self.auth_type = Razorpay::PRIVATE_AUTH
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.setup_with_oauth(access_token)
|
43
|
+
self.access_token = access_token
|
44
|
+
self.auth_type = Razorpay::OAUTH
|
38
45
|
end
|
39
46
|
|
40
47
|
def self.headers=(headers = {})
|
@@ -0,0 +1,8 @@
|
|
1
|
+
{
|
2
|
+
"public_token": "rzp_test_oauth_9xu1rkZqoXlClS",
|
3
|
+
"token_type": "Bearer",
|
4
|
+
"expires_in": 7862400,
|
5
|
+
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IkY1Z0NQYkhhRzRjcUpnIn0.eyJhdWQiOiJGNFNNeEgxanMxbkpPZiIsImp0aSI6IkY1Z0NQYkhhRzRjcUpnIiwiaWF0IjoxNTkyODMxMDExLCJuYmYiOjE1OTI4MzEwMTEsInN1YiI6IiIsImV4cCI6MTYwMDc3OTgxMSwidXNlcl9pZCI6IkYycVBpejJEdzRPRVFwIiwibWVyY2hhbnRfaWQiOiJGMnFQaVZ3N0lNV01GSyIsInNjb3BlcyI6WyJyZWFkX29ubHkiXX0.Wwqt5czhoWpVzP5_aoiymKXoGj-ydo-4A_X2jf_7rrSvk4pXdqzbA5BMrHxPdPbeFQWV6vsnsgbf99Q3g-W4kalHyH67LfAzc3qnJ-mkYDkFY93tkeG-MCco6GJW-Jm8xhaV9EPUak7z9J9jcdluu9rNXYMtd5qxD8auyRYhEgs",
|
6
|
+
"refresh_token": "def50200f42e07aded65a323f6c53181d802cc797b62cc5e78dd8038d6dff253e5877da9ad32f463a4da0ad895e3de298cbce40e162202170e763754122a6cb97910a1f58e2378ee3492dc295e1525009cccc45635308cce8575bdf373606c453ebb5eb2bec062ca197ac23810cf9d6cf31fbb9fcf5b7d4de9bf524c89a4aa90599b0151c9e4e2fa08acb6d2fe17f30a6cfecdfd671f090787e821f844e5d36f5eacb7dfb33d91e83b18216ad0ebeba2bef7721e10d436c3984daafd8654ed881c581d6be0bdc9ebfaee0dc5f9374d7184d60aae5aa85385690220690e21bc93209fb8a8cc25a6abf1108d8277f7c3d38217b47744d7",
|
7
|
+
"razorpay_account_id": "acc_Dhk2qDbmu6FwZH"
|
8
|
+
}
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Razorpay
|
4
|
+
# Tests for Razorpay::OauthToken
|
5
|
+
class RazorpayOAuthTokenTest < Minitest::Test
|
6
|
+
class OAuthToken < Razorpay::Entity; end
|
7
|
+
|
8
|
+
def test_get_auth_url
|
9
|
+
options = {
|
10
|
+
'client_id' => '8DXCMTshWSWECc',
|
11
|
+
'redirect_uri' => 'https://example.com/razorpay_callback',
|
12
|
+
'state' => 'NOBYtv8r6c75ex6WZ',
|
13
|
+
'scopes' => ["read_write"]
|
14
|
+
}
|
15
|
+
|
16
|
+
expected_auth_url = "https://auth.razorpay.com/authorize?response_type=code&client_id=8DXCMTshWSWECc&redirect_uri=https%3A%2F%2Fexample.com%2Frazorpay_callback&state=NOBYtv8r6c75ex6WZ&scope%5B%5D=read_write"
|
17
|
+
auth_url = Razorpay::OAuthToken.get_auth_url(options)
|
18
|
+
assert_equal expected_auth_url, auth_url
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_request_validation_for_get_auth_url
|
22
|
+
options = {
|
23
|
+
'client_id' => '8DXCMTshWSWECc',
|
24
|
+
'redirect_uri' => 'https://example.com/razorpay_callback',
|
25
|
+
'scopes' => ["read_write"]
|
26
|
+
}
|
27
|
+
assert_raises(Razorpay::Error) do
|
28
|
+
Razorpay::OAuthToken.get_auth_url(options)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_get_access_token
|
33
|
+
options = {
|
34
|
+
'client_id' => '8DXCMTshWSWECc',
|
35
|
+
'client_secret' => 'AESSECRETKEY',
|
36
|
+
'grant_type' => 'client_credentials',
|
37
|
+
'redirect_uri' => 'http://example.com/razorpay_callback',
|
38
|
+
'mode' => 'test'
|
39
|
+
}
|
40
|
+
stub_post(/token$/,'fake_oauth_token',options)
|
41
|
+
oauth_token = Razorpay::OAuthToken.get_access_token(options)
|
42
|
+
assert_instance_of Razorpay::Entity, oauth_token, 'OAuthToken not an instance of Entity class'
|
43
|
+
assert_equal 'rzp_test_oauth_9xu1rkZqoXlClS', oauth_token.public_token, 'Public Tokens do not match'
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_get_access_token_validation_failure
|
47
|
+
options = {
|
48
|
+
'client_id' => '8DXCMTshWSWECc',
|
49
|
+
'grant_type' => 'client_credentials',
|
50
|
+
'redirect_uri' => 'http://example.com/razorpay_callback',
|
51
|
+
'mode' => 'test'
|
52
|
+
}
|
53
|
+
assert_raises(Razorpay::Error) do
|
54
|
+
Razorpay::OAuthToken.get_access_token(options)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_refresh_token
|
59
|
+
options = {
|
60
|
+
'client_id' => '8DXCMTshWSWECc',
|
61
|
+
'client_secret' => 'AESSECRETKEY',
|
62
|
+
'refresh_token' => 'def5020096e1c470c901d34cd60fa53abdaf3662sa0'
|
63
|
+
}
|
64
|
+
expected_request_payload = options.merge('grant_type': 'refresh_token')
|
65
|
+
stub_post(/token$/,'fake_oauth_token',expected_request_payload)
|
66
|
+
oauth_token = Razorpay::OAuthToken.refresh_token(options)
|
67
|
+
assert_instance_of Razorpay::Entity, oauth_token, 'OAuthToken not an instance of Entity class'
|
68
|
+
assert_equal 'rzp_test_oauth_9xu1rkZqoXlClS', oauth_token.public_token, 'Public Tokens do not match'
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_refresh_token_validation_failure
|
72
|
+
options = {
|
73
|
+
'client_id' => '8DXCMTshWSWECc',
|
74
|
+
'refresh_token' => 'def5020096e1c470c901d34cd60fa53abdaf3662sa0'
|
75
|
+
}
|
76
|
+
assert_raises(Razorpay::Error) do
|
77
|
+
Razorpay::OAuthToken.refresh_token(options)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_revoke_token
|
82
|
+
options = {
|
83
|
+
'client_id' => '8DXCMTshWSWECc',
|
84
|
+
'client_secret' => 'AESSECRETKEY',
|
85
|
+
'token_type_hint' => 'access_token',
|
86
|
+
'token' => 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJKQTFwODVudE1ySEpoQSIsImp0aSI6IkpPZkd0aHFDTmhqQUhTIiwiaWF0IjoxNjUxMTI0NTU0LCJuYmYiOjE2NTExMjQ1NTQsInN1YiI6IiIsImV4cCI6MTY1ODk4Njk1Miw'
|
87
|
+
}
|
88
|
+
stub_post(/revoke$/,'fake_revoke_token',options)
|
89
|
+
oauth_token = Razorpay::OAuthToken.revoke_token(options)
|
90
|
+
assert_instance_of Razorpay::Entity, oauth_token, 'OAuthToken not an instance of Entity class'
|
91
|
+
assert_equal 'Token Revoked', oauth_token.message, 'Messages do not match'
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_revoke_token_validation_failure
|
95
|
+
options = {
|
96
|
+
'client_id' => '8DXCMTshWSWECc',
|
97
|
+
'client_secret' => 'AESSECRETKEY',
|
98
|
+
'token_type_hint' => 'access_token'
|
99
|
+
}
|
100
|
+
assert_raises(Razorpay::Error) do
|
101
|
+
Razorpay::OAuthToken.revoke_token(options)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'razorpay/validation_config'
|
3
|
+
|
4
|
+
module Razorpay
|
5
|
+
# Tests for Razorpay::PayloadValidator
|
6
|
+
class RazorpayPayloadValidatorTest < Minitest::Test
|
7
|
+
def test_validate_mode
|
8
|
+
payload = {
|
9
|
+
'mode1' => 'test',
|
10
|
+
'mode2' => 'live'
|
11
|
+
}
|
12
|
+
assert_silent do
|
13
|
+
Razorpay::PayloadValidator.validate(payload, [
|
14
|
+
Razorpay::ValidationConfig.new('mode1', [:mode]),
|
15
|
+
Razorpay::ValidationConfig.new('mode2', [:mode]),
|
16
|
+
])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_mode_validation_failure
|
21
|
+
payload = {
|
22
|
+
'mode' => 'testvalue'
|
23
|
+
}
|
24
|
+
assert_raises(Razorpay::Error) do
|
25
|
+
Razorpay::PayloadValidator.validate(payload, [
|
26
|
+
Razorpay::ValidationConfig.new('mode', [:mode])
|
27
|
+
])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_url_validation_failure
|
32
|
+
payload = {
|
33
|
+
'redirect_uri' => 'test.com'
|
34
|
+
}
|
35
|
+
assert_raises(Razorpay::Error) do
|
36
|
+
Razorpay::PayloadValidator.validate(payload, [
|
37
|
+
Razorpay::ValidationConfig.new('redirect_uri', [:url])
|
38
|
+
])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_non_null_validation_failure
|
43
|
+
assert_raises(Razorpay::Error) do
|
44
|
+
Razorpay::PayloadValidator.validate({}, [
|
45
|
+
Razorpay::ValidationConfig.new('redirect_uri', [:non_null])
|
46
|
+
])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_id_validation_failure
|
51
|
+
payload = {
|
52
|
+
'client_id' => 'fjidhf'
|
53
|
+
}
|
54
|
+
assert_raises(Razorpay::Error) do
|
55
|
+
Razorpay::PayloadValidator.validate({}, [
|
56
|
+
Razorpay::ValidationConfig.new('client_id', [:id])
|
57
|
+
])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -49,5 +49,22 @@ module Razorpay
|
|
49
49
|
headers: headers,
|
50
50
|
times: 1
|
51
51
|
end
|
52
|
+
|
53
|
+
def test_oauth_setup
|
54
|
+
Razorpay.setup_with_oauth('access_token')
|
55
|
+
assert_equal 'access_token', Razorpay.access_token
|
56
|
+
end
|
57
|
+
|
58
|
+
# # We mock this request
|
59
|
+
def test_auth_header_and_user_agent_for_oauth
|
60
|
+
stub_get(/$/, 'hello_response')
|
61
|
+
Razorpay.setup_with_oauth('access_token')
|
62
|
+
Razorpay::Request.new('dummy').make_test_request
|
63
|
+
user_agent = "Razorpay-Ruby/#{Razorpay::VERSION}; Ruby/#{RUBY_VERSION}"
|
64
|
+
headers = { 'User-Agent' => user_agent, 'Authorization' => 'Bearer access_token' }
|
65
|
+
assert_requested :get, 'https://api.razorpay.com/',
|
66
|
+
headers: headers,
|
67
|
+
times: 1
|
68
|
+
end
|
52
69
|
end
|
53
70
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'test_helper'
|
2
|
+
require 'json'
|
2
3
|
|
3
4
|
module Razorpay
|
4
5
|
# Tests for Razorpay::Utility
|
@@ -80,5 +81,38 @@ module Razorpay
|
|
80
81
|
Razorpay::Utility.verify_webhook_signature(webhook_body, signature, secret)
|
81
82
|
end
|
82
83
|
end
|
84
|
+
|
85
|
+
def test_generate_onboarding_signature
|
86
|
+
secret = "EnLs21M47BllR3X8PSFtjtbd"
|
87
|
+
timestamp = Time.now.to_i
|
88
|
+
body = {
|
89
|
+
submerchant_id: 'NSgKfYIR2f9v2y',
|
90
|
+
timestamp: timestamp
|
91
|
+
}
|
92
|
+
encryptedData = Razorpay::Utility.generate_onboarding_signature(body, secret)
|
93
|
+
json_data = decrypt(encryptedData, secret)
|
94
|
+
body = JSON.parse(json_data)
|
95
|
+
assert_equal 'NSgKfYIR2f9v2y', body['submerchant_id'], 'Submerchant IDs do not match'
|
96
|
+
assert_equal timestamp, body['timestamp'], 'Timestamps do not match'
|
97
|
+
end
|
98
|
+
|
99
|
+
def decrypt(data, secret)
|
100
|
+
combined_encrypted_data = [data].pack("H*")
|
101
|
+
|
102
|
+
iv = secret[0, 12]
|
103
|
+
key = secret[0, 16]
|
104
|
+
tag = combined_encrypted_data[-16..]
|
105
|
+
|
106
|
+
encrypted_data = combined_encrypted_data[0...-16]
|
107
|
+
|
108
|
+
cipher = OpenSSL::Cipher.new('aes-128-gcm')
|
109
|
+
cipher.decrypt
|
110
|
+
cipher.key = key
|
111
|
+
cipher.iv = iv
|
112
|
+
cipher.auth_tag = tag
|
113
|
+
cipher.auth_data = ""
|
114
|
+
|
115
|
+
cipher.update(encrypted_data) + cipher.final
|
116
|
+
end
|
83
117
|
end
|
84
118
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: razorpay
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.2.
|
4
|
+
version: 3.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Abhay Rana
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2024-04-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: httparty
|
@@ -138,6 +138,7 @@ files:
|
|
138
138
|
- documents/emandate.md
|
139
139
|
- documents/fund.md
|
140
140
|
- documents/items.md
|
141
|
+
- documents/oauth_token.md
|
141
142
|
- documents/order.md
|
142
143
|
- documents/papernach.md
|
143
144
|
- documents/payment.md
|
@@ -176,7 +177,9 @@ files:
|
|
176
177
|
- lib/razorpay/iin.rb
|
177
178
|
- lib/razorpay/invoice.rb
|
178
179
|
- lib/razorpay/item.rb
|
180
|
+
- lib/razorpay/oauth_token.rb
|
179
181
|
- lib/razorpay/order.rb
|
182
|
+
- lib/razorpay/payload_validator.rb
|
180
183
|
- lib/razorpay/payment.rb
|
181
184
|
- lib/razorpay/payment_link.rb
|
182
185
|
- lib/razorpay/payment_method.rb
|
@@ -192,6 +195,7 @@ files:
|
|
192
195
|
- lib/razorpay/token.rb
|
193
196
|
- lib/razorpay/transfer.rb
|
194
197
|
- lib/razorpay/utility.rb
|
198
|
+
- lib/razorpay/validation_config.rb
|
195
199
|
- lib/razorpay/virtual_account.rb
|
196
200
|
- lib/razorpay/webhook.rb
|
197
201
|
- razorpay-ruby.gemspec
|
@@ -219,6 +223,7 @@ files:
|
|
219
223
|
- test/fixtures/fake_instant_settlement.json
|
220
224
|
- test/fixtures/fake_invoice.json
|
221
225
|
- test/fixtures/fake_item.json
|
226
|
+
- test/fixtures/fake_oauth_token.json
|
222
227
|
- test/fixtures/fake_order.json
|
223
228
|
- test/fixtures/fake_order_transfers.json
|
224
229
|
- test/fixtures/fake_otp_generate.json
|
@@ -236,6 +241,7 @@ files:
|
|
236
241
|
- test/fixtures/fake_recurring.json
|
237
242
|
- test/fixtures/fake_refund.json
|
238
243
|
- test/fixtures/fake_refunded_payment.json
|
244
|
+
- test/fixtures/fake_revoke_token.json
|
239
245
|
- test/fixtures/fake_settlement.json
|
240
246
|
- test/fixtures/fake_settlement_on_demand.json
|
241
247
|
- test/fixtures/fake_stakeholder.json
|
@@ -296,7 +302,9 @@ files:
|
|
296
302
|
- test/razorpay/test_iin.rb
|
297
303
|
- test/razorpay/test_invoice.rb
|
298
304
|
- test/razorpay/test_item.rb
|
305
|
+
- test/razorpay/test_oauth_token.rb
|
299
306
|
- test/razorpay/test_order.rb
|
307
|
+
- test/razorpay/test_payload_validator.rb
|
300
308
|
- test/razorpay/test_payment.rb
|
301
309
|
- test/razorpay/test_payment_link.rb
|
302
310
|
- test/razorpay/test_plan.rb
|
@@ -363,6 +371,7 @@ test_files:
|
|
363
371
|
- test/fixtures/fake_instant_settlement.json
|
364
372
|
- test/fixtures/fake_invoice.json
|
365
373
|
- test/fixtures/fake_item.json
|
374
|
+
- test/fixtures/fake_oauth_token.json
|
366
375
|
- test/fixtures/fake_order.json
|
367
376
|
- test/fixtures/fake_order_transfers.json
|
368
377
|
- test/fixtures/fake_otp_generate.json
|
@@ -380,6 +389,7 @@ test_files:
|
|
380
389
|
- test/fixtures/fake_recurring.json
|
381
390
|
- test/fixtures/fake_refund.json
|
382
391
|
- test/fixtures/fake_refunded_payment.json
|
392
|
+
- test/fixtures/fake_revoke_token.json
|
383
393
|
- test/fixtures/fake_settlement.json
|
384
394
|
- test/fixtures/fake_settlement_on_demand.json
|
385
395
|
- test/fixtures/fake_stakeholder.json
|
@@ -440,7 +450,9 @@ test_files:
|
|
440
450
|
- test/razorpay/test_iin.rb
|
441
451
|
- test/razorpay/test_invoice.rb
|
442
452
|
- test/razorpay/test_item.rb
|
453
|
+
- test/razorpay/test_oauth_token.rb
|
443
454
|
- test/razorpay/test_order.rb
|
455
|
+
- test/razorpay/test_payload_validator.rb
|
444
456
|
- test/razorpay/test_payment.rb
|
445
457
|
- test/razorpay/test_payment_link.rb
|
446
458
|
- test/razorpay/test_plan.rb
|