parsbank 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +148 -64
- data/lib/configuration.rb +3 -1
- data/lib/parsbank/restfull.rb +118 -71
- data/lib/parsbank/version.rb +1 -1
- data/lib/parsbank.rb +143 -104
- data/lib/psp.json +78 -0
- metadata +4 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9f5826abb8b78b831b9be8e8565e004863a75b7c64ff44beabd899a93bbafa4
|
4
|
+
data.tar.gz: 2c6ad504a2e26757a781c144844843f224da7ee57d80346d09dd74b4b08acac4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6814ce0975c37dc51992680307ed6dc411ce7e075d12ae1c60b15a649e0a4e303f3cb18a9a47eb5f49d04ccc6d52fccb8caaf441ca2288d67f431d6691c966a9
|
7
|
+
data.tar.gz: c0d2a6b4aeef2f6f325e18d322016b7643a1f4c89f7c05a37a9f915625823b25c2552f681f1c9a37fe77febfc6b795a0f0811486de99b6f3ff18beea001f55f5
|
data/README.md
CHANGED
@@ -1,111 +1,195 @@
|
|
1
1
|
# ParsBank
|
2
2
|
==============
|
3
3
|
|
4
|
-
[![Gem Version](https://badge.fury.io/rb/parsbank.svg)](https://rubygems.org/gems/parsbank)
|
5
|
-
![Build](https://github.com/abrfanet/ParsBank/workflows/CI/badge.svg)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/parsbank.svg)](https://rubygems.org/gems/parsbank)
|
5
|
+
![Build Status](https://github.com/abrfanet/ParsBank/workflows/CI/badge.svg)
|
6
6
|
|
7
|
+
**ParsBank Gateway** is a Ruby gem designed to integrate with WSDL and JSON APIs of all payments methods such as cryptocurrency platforms (e.g., Bitcoin, USDT), and traditional payment platforms like Stripe and PayPal or private banks such as Troy bank or persians banks. This gem leverages **SOAP** and **Faraday** libraries, optimized for multiple retries during connection failures or timeouts. Additionally, it includes a proxy wrapper for connecting to core banks via MITM (man-in-the-middle) servers.
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
An Ruby Gem Library for integrate with WSDL and JSON of Persian Banks, In this Gem we use soap and faraday lib as main dependency also we tunned soap/faraday for multile retries when failed connections or request, in the end we work on proxy wrapper for connct to core bank with MITM server
|
9
|
+
---
|
11
10
|
|
12
11
|
## Installation
|
13
12
|
|
14
|
-
|
13
|
+
### Using Bundler
|
14
|
+
Add ParsBank to your application's Gemfile and install it with:
|
15
15
|
|
16
|
-
|
16
|
+
```bash
|
17
|
+
$ bundle add ParsBank
|
18
|
+
```
|
17
19
|
|
18
|
-
|
20
|
+
### Without Bundler
|
21
|
+
Install the gem directly:
|
19
22
|
|
20
|
-
|
23
|
+
```bash
|
24
|
+
$ gem install ParsBank
|
25
|
+
```
|
21
26
|
|
22
|
-
|
27
|
+
### For Sinatra Applications
|
28
|
+
Include the gem in your Gemfile:
|
23
29
|
|
24
|
-
|
30
|
+
```ruby
|
31
|
+
gem "ParsBank"
|
32
|
+
```
|
25
33
|
|
26
|
-
|
34
|
+
Then install it with:
|
27
35
|
|
28
|
-
|
36
|
+
```bash
|
37
|
+
$ bundle install
|
29
38
|
```
|
30
|
-
#config/initilizers/pars_bank.rb
|
31
39
|
|
40
|
+
---
|
41
|
+
|
42
|
+
## Usage
|
32
43
|
|
44
|
+
### Step 1: Configure ParsBank
|
45
|
+
Create a configuration file in `config/initializers` (e.g., `config/initializers/pars_bank.rb`):
|
46
|
+
|
47
|
+
```ruby
|
33
48
|
ParsBank.configuration do |config|
|
49
|
+
config.callback_url = 'https://example.com/CallBack' # Your callback URL
|
50
|
+
config.debug = false # Enable logging (Rails logs and STDOUT)
|
51
|
+
config.sandbox = false # Simulate requests and auto-approve callbacks
|
52
|
+
config.webhook = 'https://yoursite.com/income-webhook?title=TITLE&message=MESSAGE' # Transaction notification webhook
|
53
|
+
config.webhook_method = 'GET' # Webhook HTTP method (GET or POST)
|
54
|
+
config.mitm_server = 'https://your-mitm-server.com' # MITM server location
|
55
|
+
config.secrets_path = Rails.root.join('config/bank_secrets.yaml') # Path to bank credentials (e.g., merchant ID, tokens)
|
56
|
+
|
57
|
+
# Web Panel Configuration
|
58
|
+
config.webpanel_path = '/parsbank' # Web panel path
|
59
|
+
config.username = ENV['PARSBANK_USERNAME'] # Web panel username
|
60
|
+
config.password = ENV['PARSBANK_PASSWORD'] # Web panel password
|
61
|
+
config.allowed_ips = ['192.168.10.10'] # Restrict access by IP (use '*' to allow all)
|
62
|
+
config.allow_when = ->(username, password) { # Authentication using a Rails model
|
63
|
+
user = User.find_by(username: username)
|
64
|
+
user&.authenticate(password) && user.admin?
|
65
|
+
}
|
66
|
+
config.captcha = false # Enable CAPTCHA for security
|
67
|
+
config.model = Transaction # Define transaction model (must include fields like amount, status, etc.)
|
68
|
+
end
|
69
|
+
```
|
34
70
|
|
35
|
-
|
71
|
+
---
|
72
|
+
|
73
|
+
### Step 2: Use ParsBank in Your Controller
|
74
|
+
|
75
|
+
Use the `get_redirect_from` method to generate a redirect form for users:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
class CartController < ApplicationController
|
79
|
+
# @INPUT fiat_amount: can be dollars or rials
|
80
|
+
# @INPUT crypto_amount: can be crypto assets amount like 0.0005
|
81
|
+
# @INPUT real_amount: When use crypto or Rials can use dollar instead amount of crypto (E.G 100 dollar equal 0.005 bitcoin)
|
82
|
+
# @INPUT description(required): Explain transaction details
|
83
|
+
# @INPUT bank: Select specific bank or payment method like 'bsc-binance', 'nobitex', 'zarinpal', 'perfect-money'
|
84
|
+
# @INPUT tags: Used for call which payments method such as ['crypto','rls','dollar','persian-banks','russian-banks']
|
85
|
+
# @OUTPUT get_redirect_from: an javascript code for redirect user to gateways
|
86
|
+
def redirect_to_parsbank
|
87
|
+
form = ParsBank.get_redirect_from(fiat_amount: '10', description: 'Charge Account')
|
88
|
+
render html: form
|
89
|
+
end
|
90
|
+
|
91
|
+
# @DESC: Shortcode for get all of enabled paymetns methods
|
92
|
+
# @INPUT tags(Optional): ['crypto','rls','dollar','persian-banks','russian-banks']
|
93
|
+
# @OUTPUT gateways_list_shortcode: List of all banks with name and logo with ul wrapper as html
|
94
|
+
def choose_payment
|
95
|
+
@payments_list_available_shortcode = ParsBank.gateways_list_shortcode
|
96
|
+
end
|
97
|
+
|
98
|
+
# @DESC: Parse all returned params from banks for verify transaction
|
99
|
+
# @OUTPUT verify_transaction: Return transaction status as json like {status: 200, message: 'Payment Successfull'}
|
100
|
+
def callback
|
101
|
+
@parsbank_verifier= ParsBank.verify_transaction
|
102
|
+
if @parsbank_verifier[:status] == 200
|
103
|
+
flash[:success]= @parsbank_verifier[:message]
|
104
|
+
redirect_to Something_path
|
105
|
+
else
|
106
|
+
flash[:error]= @parsbank_verifier[:message]
|
107
|
+
redirect_to Something_path
|
108
|
+
end
|
109
|
+
end
|
36
110
|
|
37
|
-
config.debug = false # Enable Log Tracking with Rails.log and STDOUT
|
38
111
|
|
39
|
-
|
112
|
+
end
|
113
|
+
```
|
40
114
|
|
41
|
-
|
42
|
-
config.webhook_method = 'GET' # or POST
|
115
|
+
---
|
43
116
|
|
44
|
-
|
117
|
+
## ParsBank Amazing Web
|
45
118
|
|
46
|
-
|
119
|
+
ParsBank comes with a built-in **web dashboard** for managing transactions and configurations visually. The dashboard includes:
|
120
|
+
- A beautiful interface with **Canva graphs** for transaction analysis.
|
121
|
+
- Tools for campaign improvements and data-driven decisions.
|
47
122
|
|
48
|
-
|
123
|
+
### Security Notice
|
124
|
+
Ensure that you apply CIS and other hardening rules to secure your bank credentials and virtual accounts (e.g., Binance) when using ParsBank Web.
|
49
125
|
|
50
|
-
|
51
|
-
config.webpanel_path = '/parsbank'
|
52
|
-
## Basic Authentication
|
53
|
-
config.username = ENV['PARSBANK_USERNAME']
|
54
|
-
config.password = ENV['PARSBANK_PASSWORD']
|
55
|
-
## Authetication With IP source
|
56
|
-
config.allowed_ips = ['192.168.10.10'] # add * to allow all ip
|
57
|
-
## Authentication with rails model
|
58
|
-
config.allow_when = User.find_by(username: USERNAME).authenticate(PASSWORD) && User.find_by(username: USERNAME).admin?
|
126
|
+
---
|
59
127
|
|
60
|
-
|
61
|
-
config.captcha = false
|
128
|
+
### Setup for ParsBank Web
|
62
129
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
130
|
+
#### Method 1: Isolated Docker Container
|
131
|
+
**Requirements**:
|
132
|
+
- Docker
|
133
|
+
- Nginx or Apache (for reverse proxy)
|
134
|
+
- Resources: Adequate CPU, RAM, and modern storage for concurrent operations.
|
67
135
|
|
68
|
-
|
136
|
+
**Steps**:
|
137
|
+
1. Clone the repository:
|
138
|
+
```bash
|
139
|
+
git clone https://github.com/Abrfanet/parsbank-web
|
140
|
+
```
|
141
|
+
2. Follow the repository's setup instructions.
|
69
142
|
|
70
|
-
|
143
|
+
#### Method 2: Inside a Rails Application
|
144
|
+
Include the web dashboard gem in your Rails app (refer to ParsBank Web documentation).
|
71
145
|
|
146
|
+
---
|
72
147
|
|
73
|
-
|
148
|
+
## 👨🏻💻 Development
|
74
149
|
|
75
|
-
|
76
|
-
class ApplicationController > Cart
|
77
|
-
def redirect_to_ParsBank
|
78
|
-
form = ParsBank.get_redirect_from(amount: 100000, description: 'Charge Account')
|
79
|
-
render html: form
|
80
|
-
end
|
81
|
-
end
|
82
|
-
```
|
150
|
+
We currently do not accept pull requests. Please report any issues in the [GitHub Issues section](https://github.com/abrfanet/ParsBank/issues). Also If you live in countries with specific payment systems, such as Russia (MIR Card), Germany, Turkey (Troy Card), or others, we warmly welcome you to join us as a developer or contributor. Your expertise and perspective can help us better support diverse payment ecosystems(We support you with PAGs users & CPC ⭐).
|
83
151
|
|
152
|
+
---
|
84
153
|
|
154
|
+
## 💖 Support This Project
|
155
|
+
|
156
|
+
If you find this project helpful and would like to support its development, consider making a donation. Your contributions help maintain and improve this project. Thank you for your generosity! 🙏
|
157
|
+
|
158
|
+
### Donate USDT (Tether)
|
159
|
+
- **USDT (ERC-20):** `0x2028f409a42413076665231e209896bbe0221d64`
|
160
|
+
- **USDT (TRC-20):** `TNtLkdy2FAKKBGJ4F8ij2mppWpjkB8GULy`
|
161
|
+
- **USDT (BEP-20):** `0x2028f409a42413076665231e209896bbe0221d64`
|
162
|
+
|
163
|
+
|
164
|
+
### Donate BTC (Bitcoin)
|
165
|
+
- **BSC (BEP-20):** `0x2028f409a42413076665231e209896bbe0221d64`
|
85
166
|
|
86
|
-
# ParsBank Amazing Web
|
87
|
-
With ParsBank Web You Can Access To Your Transactions and Config Files Visualy! Also You Get Beautifull Dashboard with Canva Graph For Analysis Your Transaction And Improve Your Campiagn And Important Decisions ⭐
|
88
167
|
|
89
|
-
```
|
90
|
-
Important Note: When Use ParsBank Web you should apply CIS rules and all harening rules for secure your credentials of banks and virtuals account like binance.
|
91
|
-
```
|
92
168
|
|
93
|
-
|
169
|
+
## 🚀 Premium Features
|
94
170
|
|
95
|
-
|
96
|
-
Requrements:
|
97
|
-
- Docker
|
98
|
-
- Nginx or Apache Reverse Proxy for forward trafik to specific port
|
99
|
-
- ParsBank Web use sinatra with Concurency so needs considerable resource like RAM, CPU or next-gen of Hard Drive
|
100
|
-
in first step clone git repository `git clone https://github.com/Abrfanet/parsbank-web`
|
171
|
+
This project offers **premium features** to enhance your experience and support ongoing development. By contributing, you'll gain access to advanced capabilities while helping sustain and improve this project. 🙌
|
101
172
|
|
173
|
+
#### 🔐 How to Access Premium Features
|
174
|
+
1. **Make a Contribution**:
|
175
|
+
- Pay 29 USDT to one of the wallet addresses below(Annualy).
|
176
|
+
2. **Send Proof of Payment**:
|
177
|
+
- Email your transaction details to `info@abrfa.net` or submit them via a dedicated contact form.
|
178
|
+
3. **Receive Access**:
|
179
|
+
- Upon verification, you'll receive instructions to access the premium features.
|
102
180
|
|
103
|
-
|
181
|
+
#### 💳 USDT Wallet Addresses
|
182
|
+
- **USDT (ERC-20):** `0x2028f409a42413076665231e209896bbe0221d64`
|
183
|
+
- **USDT (TRC-20):** `TNtLkdy2FAKKBGJ4F8ij2mppWpjkB8GULy`
|
104
184
|
|
105
|
-
|
185
|
+
#### 🔥 Available Premium Features
|
186
|
+
- Feature 1: **Advanced Analytics**
|
187
|
+
- Feature 2: **Customizable Reports**
|
188
|
+
- Feature 3: **Priority Support**
|
189
|
+
- Feature 4: **ParsBank Web Access**
|
106
190
|
|
107
|
-
|
191
|
+
> **Note**: Contributions are used to maintain and improve this project. Thank you for your support! 🙏
|
108
192
|
|
109
193
|
## Contributing
|
110
194
|
|
111
|
-
Bug reports and
|
195
|
+
Bug reports and feature requests are welcome at [ParsBank GitHub repository](https://github.com/abrfanet/ParsBank).
|
data/lib/configuration.rb
CHANGED
@@ -12,7 +12,8 @@ class Configuration
|
|
12
12
|
:allowed_ips,
|
13
13
|
:allow_when,
|
14
14
|
:captcha,
|
15
|
-
:model
|
15
|
+
:model,
|
16
|
+
:database_url
|
16
17
|
|
17
18
|
def initialize
|
18
19
|
@callback_url = nil
|
@@ -30,5 +31,6 @@ class Configuration
|
|
30
31
|
@allow_when = -> { true }
|
31
32
|
@captcha = false
|
32
33
|
@model = nil
|
34
|
+
@database_url = nil
|
33
35
|
end
|
34
36
|
end
|
data/lib/parsbank/restfull.rb
CHANGED
@@ -1,7 +1,14 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'json'
|
4
|
+
|
1
5
|
module Parsbank
|
2
6
|
class Restfull
|
3
7
|
attr_accessor :connection
|
4
8
|
|
9
|
+
MAX_RETRIES = 3
|
10
|
+
RETRY_INTERVAL = 1 # In seconds
|
11
|
+
|
5
12
|
def initialize(args = {})
|
6
13
|
@endpoint = args.fetch(:endpoint)
|
7
14
|
@action = args.fetch(:action)
|
@@ -15,17 +22,14 @@ module Parsbank
|
|
15
22
|
|
16
23
|
def call
|
17
24
|
response = send_request
|
25
|
+
log_response(response)
|
18
26
|
|
19
|
-
|
20
|
-
|
21
|
-
if response.success?
|
22
|
-
response
|
27
|
+
if response.is_a?(Net::HTTPSuccess)
|
28
|
+
parse_response(response)
|
23
29
|
else
|
24
30
|
log_and_raise_error(response)
|
25
31
|
end
|
26
|
-
rescue
|
27
|
-
handle_error("Connection failed: #{e.message}", e)
|
28
|
-
rescue Faraday::TimeoutError => e
|
32
|
+
rescue Timeout::Error => e
|
29
33
|
handle_error("Request timed out: #{e.message}", e)
|
30
34
|
rescue StandardError => e
|
31
35
|
handle_error("An unexpected error occurred: #{e.message}", e)
|
@@ -33,103 +37,146 @@ module Parsbank
|
|
33
37
|
|
34
38
|
private
|
35
39
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
webhook_url.gsub!('TITLE', "Webhook of Connection Error at #{Time.now}") if Parsbank.configuration.webhook_method == :get
|
41
|
-
|
42
|
-
connection = Faraday.new() do |conn|
|
43
|
-
conn.request :json if @response_type == :json # Automatically converts payload to JSON
|
44
|
-
conn.response :json if @response_type == :json # Automatically parses JSON response
|
45
|
-
conn.adapter Faraday.default_adapter
|
46
|
-
conn.request :retry, max: 3, interval: 0.05,
|
47
|
-
interval_randomness: 0.5, backoff_factor: 2,
|
48
|
-
exceptions: [Faraday::TimeoutError, Faraday::ConnectionFailed]
|
49
|
-
conn.use FaradayMiddleware::FollowRedirects
|
50
|
-
end
|
51
|
-
|
52
|
-
case Parsbank.configuration.webhook_method
|
53
|
-
when :post
|
54
|
-
connection.post('&parsbank') do |req|
|
55
|
-
req.headers = headers
|
56
|
-
req.body = {}
|
57
|
-
end
|
40
|
+
def setup_connection
|
41
|
+
@uri = URI.parse(@endpoint)
|
42
|
+
@connection = Net::HTTP.new(@uri.host, @uri.port)
|
43
|
+
@connection.use_ssl = true if @uri.scheme == 'https'
|
58
44
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
rescue Faraday::ConnectionFailed => e
|
63
|
-
Rails.logger.error("Webhook Connection failed: #{e.message}", e)
|
64
|
-
rescue Faraday::TimeoutError => e
|
65
|
-
Rails.logger.error("Webhook Request timed out: #{e.message}", e)
|
66
|
-
rescue StandardError => e
|
67
|
-
Rails.logger.error("Webhook An unexpected error occurred: #{e.message}", e)
|
45
|
+
# Setting timeouts
|
46
|
+
@connection.open_timeout = 10 # Time to wait for the connection to open
|
47
|
+
@connection.read_timeout = 10 # Time to wait for a response
|
68
48
|
end
|
69
49
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
50
|
+
def send_request
|
51
|
+
retries = 0
|
52
|
+
begin
|
53
|
+
request = build_request
|
54
|
+
@connection.start do
|
55
|
+
response = @connection.request(request)
|
56
|
+
|
57
|
+
# Handling redirects manually (max 5 redirects)
|
58
|
+
handle_redirects(response)
|
59
|
+
|
60
|
+
return response
|
61
|
+
end
|
62
|
+
rescue Timeout::Error => e
|
63
|
+
retries += 1
|
64
|
+
if retries < MAX_RETRIES
|
65
|
+
log_to_rails("Timeout occurred. Retrying... (#{retries}/#{MAX_RETRIES})", :warn)
|
66
|
+
sleep(RETRY_INTERVAL)
|
67
|
+
retry
|
68
|
+
else
|
69
|
+
raise "Request timed out after #{MAX_RETRIES} retries: #{e.message}"
|
70
|
+
end
|
71
|
+
rescue StandardError => e
|
72
|
+
log_to_rails("Request failed: #{e.message}", :error)
|
73
|
+
raise e
|
79
74
|
end
|
80
75
|
end
|
81
76
|
|
82
|
-
def
|
83
|
-
headers = default_headers.merge(@headers)
|
84
|
-
|
77
|
+
def build_request
|
85
78
|
case @http_method
|
86
79
|
when :post
|
87
|
-
|
80
|
+
build_post_request
|
88
81
|
when :get
|
89
|
-
|
82
|
+
build_get_request
|
90
83
|
when :options
|
91
|
-
|
84
|
+
build_options_request
|
92
85
|
else
|
93
86
|
raise ArgumentError, "HTTP Method Not Allowed: #{@http_method}"
|
94
87
|
end
|
95
88
|
end
|
96
89
|
|
97
|
-
def
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
end
|
90
|
+
def build_post_request
|
91
|
+
request = Net::HTTP::Post.new(@action, default_headers)
|
92
|
+
request.body = @request_message.to_json if @request_message.any?
|
93
|
+
request
|
102
94
|
end
|
103
95
|
|
104
|
-
def
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
end
|
96
|
+
def build_get_request
|
97
|
+
request = Net::HTTP::Get.new(@action, default_headers)
|
98
|
+
request.set_form_data(@request_message) if @request_message.any?
|
99
|
+
request
|
109
100
|
end
|
110
101
|
|
111
|
-
def
|
112
|
-
|
113
|
-
req.headers = headers
|
114
|
-
end
|
102
|
+
def build_options_request
|
103
|
+
Net::HTTP::Options.new(@action, default_headers)
|
115
104
|
end
|
116
105
|
|
117
106
|
def default_headers
|
118
107
|
{
|
119
108
|
'Content-Type' => 'application/json',
|
120
109
|
'Parsbank-RubyGem' => Parsbank::VERSION
|
121
|
-
}
|
110
|
+
}.merge(@headers)
|
111
|
+
end
|
112
|
+
|
113
|
+
def log_response(response)
|
114
|
+
log_to_rails("Received response with status: #{response.code}, body: #{response.body.inspect}", :info)
|
115
|
+
end
|
116
|
+
|
117
|
+
def parse_response(response)
|
118
|
+
return response.body if @response_type == :raw
|
119
|
+
|
120
|
+
case @response_type
|
121
|
+
when :json
|
122
|
+
JSON.parse(response.body)
|
123
|
+
else
|
124
|
+
response.body
|
125
|
+
end
|
122
126
|
end
|
123
127
|
|
124
128
|
def log_and_raise_error(response)
|
125
|
-
|
126
|
-
raise "API request failed with status #{response.
|
129
|
+
log_to_rails("Request to #{@endpoint}/#{@action} failed with status: #{response.code}, error: #{response.body.inspect}", :error)
|
130
|
+
raise "API request failed with status #{response.code}: #{response.body}"
|
127
131
|
end
|
128
132
|
|
129
133
|
def handle_error(message, exception)
|
130
|
-
|
134
|
+
log_to_rails(message, :error)
|
131
135
|
webhook(message) if Parsbank.configuration.webhook.present?
|
132
136
|
raise exception
|
133
137
|
end
|
138
|
+
|
139
|
+
def log_to_rails(message, level = :info)
|
140
|
+
case level
|
141
|
+
when :error
|
142
|
+
Rails.logger.error(message) if defined?(Rails)
|
143
|
+
when :warn
|
144
|
+
Rails.logger.warn(message) if defined?(Rails)
|
145
|
+
else
|
146
|
+
Rails.logger.info(message) if defined?(Rails)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def webhook(message)
|
151
|
+
webhook_url = Parsbank.configuration.webhook
|
152
|
+
webhook_url.gsub!('MESSAGE', message) if Parsbank.configuration.webhook_method == :get
|
153
|
+
webhook_url.gsub!('TITLE', "Webhook of Connection Error at #{Time.now}") if Parsbank.configuration.webhook_method == :get
|
154
|
+
|
155
|
+
uri = URI.parse(webhook_url)
|
156
|
+
connection = Net::HTTP.new(uri.host, uri.port)
|
157
|
+
connection.use_ssl = uri.scheme == 'https'
|
158
|
+
|
159
|
+
case Parsbank.configuration.webhook_method
|
160
|
+
when :post
|
161
|
+
request = Net::HTTP::Post.new(uri.path, default_headers)
|
162
|
+
request.body = {}.to_json
|
163
|
+
connection.request(request)
|
164
|
+
when :get
|
165
|
+
request = Net::HTTP::Get.new(uri.path, default_headers)
|
166
|
+
connection.request(request)
|
167
|
+
end
|
168
|
+
rescue StandardError => e
|
169
|
+
log_to_rails("Webhook Error: #{e.message}", :error)
|
170
|
+
end
|
171
|
+
|
172
|
+
def handle_redirects(response)
|
173
|
+
if response.is_a?(Net::HTTPRedirection)
|
174
|
+
location = response['Location']
|
175
|
+
log_to_rails("Redirecting to: #{location}", :warn)
|
176
|
+
redirect_uri = URI.parse(location)
|
177
|
+
request = Net::HTTP::Get.new(redirect_uri)
|
178
|
+
@connection.request(request)
|
179
|
+
end
|
180
|
+
end
|
134
181
|
end
|
135
182
|
end
|
data/lib/parsbank/version.rb
CHANGED
data/lib/parsbank.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'yaml'
|
4
4
|
require 'savon'
|
5
|
+
require 'uri'
|
6
|
+
require 'active_support'
|
5
7
|
require 'parsbank/version'
|
6
8
|
require 'parsbank/restfull'
|
7
9
|
require 'parsbank/bsc-bitcoin/bsc-bitcoin'
|
@@ -14,88 +16,8 @@ require 'configuration'
|
|
14
16
|
module Parsbank
|
15
17
|
class Error < StandardError; end
|
16
18
|
|
17
|
-
$SUPPORTED_PSP =
|
18
|
-
|
19
|
-
'name': 'Asan Pardakht CO.',
|
20
|
-
'website': 'http://asanpardakht.ir',
|
21
|
-
'tags': %w[iranian-psp ir rial]
|
22
|
-
},
|
23
|
-
'damavand': {
|
24
|
-
'name': 'Electronic Card Damavand CO.',
|
25
|
-
'website': 'http://ecd-co.ir',
|
26
|
-
'tags': %w[iranian-psp ir rial]
|
27
|
-
},
|
28
|
-
'mellat': {
|
29
|
-
'name': 'Behpardakht Mellat CO.',
|
30
|
-
'website': 'http://behpardakht.com',
|
31
|
-
'tags': %w[iranian-psp ir rial]
|
32
|
-
},
|
33
|
-
'pep': {
|
34
|
-
'name': 'Pasargad CO.',
|
35
|
-
'website': 'http://pep.co.ir',
|
36
|
-
'tags': %w[iranian-psp ir rial]
|
37
|
-
},
|
38
|
-
|
39
|
-
'sep': {
|
40
|
-
'name': 'Saman Bank CO.',
|
41
|
-
'website': 'http://sep.ir',
|
42
|
-
'tags': %w[iranian-psp ir rial]
|
43
|
-
},
|
44
|
-
'pna': {
|
45
|
-
'name': 'Pardakht Novin Arian CO.',
|
46
|
-
'website': 'http://pna.co.ir',
|
47
|
-
'tags': %w[iranian-psp ir rial]
|
48
|
-
},
|
49
|
-
'pec': {
|
50
|
-
'name': 'Parsian Bank CO.',
|
51
|
-
'website': 'http://pec.ir',
|
52
|
-
'tags': %w[iranian-psp ir rial]
|
53
|
-
},
|
54
|
-
|
55
|
-
'sadad': {
|
56
|
-
'name': 'Sadad Bank CO.',
|
57
|
-
'website': 'http://sadadco.com',
|
58
|
-
'tags': %w[iranian-psp ir rial]
|
59
|
-
},
|
60
|
-
'sayan': {
|
61
|
-
'name': 'Sayan Card CO.',
|
62
|
-
'website': 'http://sayancard.ir',
|
63
|
-
'tags': %w[iranian-psp ir rial]
|
64
|
-
},
|
65
|
-
|
66
|
-
'fanava': {
|
67
|
-
'name': 'Fan Ava Card CO.',
|
68
|
-
'website': 'http://fanavacard.com',
|
69
|
-
'tags': %w[iranian-psp ir rial]
|
70
|
-
},
|
71
|
-
'kiccc': {
|
72
|
-
'name': 'IranKish CO.',
|
73
|
-
'website': 'http://kiccc.com',
|
74
|
-
'tags': %w[iranian-psp ir rial]
|
75
|
-
},
|
76
|
-
|
77
|
-
'sepehr': {
|
78
|
-
'name': 'Sepehr Bank CO.',
|
79
|
-
'website': 'http://www.sepehrpay.com',
|
80
|
-
'tags': %w[iranian-psp ir rial]
|
81
|
-
},
|
82
|
-
|
83
|
-
'zarinpal': {
|
84
|
-
'name': 'Zarinpal',
|
85
|
-
'website': 'http://www.sepehrpay.com',
|
86
|
-
'tags': %w[iranian-psp ir rial]
|
87
|
-
},
|
88
|
-
'zibal': {
|
89
|
-
'name': 'Zibal',
|
90
|
-
'website': 'http://zibal.ir',
|
91
|
-
'tags': %w[iranian-psp ir rial]
|
92
|
-
},
|
93
|
-
'bscbitcoin': {
|
94
|
-
'name': 'Binance Bitcoin',
|
95
|
-
'website': 'https://bitcoin.org',
|
96
|
-
'tags': %w[btc bitcoin binance bsc crypto]
|
97
|
-
}
|
98
|
-
]
|
19
|
+
$SUPPORTED_PSP = JSON.parse(File.read(File.join(__dir__, 'psp.json')))
|
20
|
+
|
99
21
|
class << self
|
100
22
|
attr_accessor :configuration
|
101
23
|
end
|
@@ -114,41 +36,146 @@ module Parsbank
|
|
114
36
|
end
|
115
37
|
|
116
38
|
|
117
|
-
def self.
|
118
|
-
|
119
|
-
|
120
|
-
|
39
|
+
def self.initialize_in_rails
|
40
|
+
return unless defined?(Rails)
|
41
|
+
|
42
|
+
ActiveSupport.on_load(:after_initialize) do
|
43
|
+
Parsbank.initialize!
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.initialize!
|
48
|
+
if Parsbank.configuration.database_url.present?
|
49
|
+
establish_connection
|
50
|
+
else
|
51
|
+
puts "\033[31mParsBank ERROR: database_url not set, Transaction history not stored on database\033[0m"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.establish_connection
|
56
|
+
database_url = Parsbank.configuration.database_url
|
57
|
+
model = Parsbank.configuration.model
|
58
|
+
raise "DATABASE_URL environment variable is not set" if database_url.nil?
|
59
|
+
|
60
|
+
supported_databases = {
|
61
|
+
'postgresql' => 'pg',
|
62
|
+
'mysql2' => 'mysql2',
|
63
|
+
'sqlite3' => 'sqlite3',
|
64
|
+
'nulldb' => 'nulldb'
|
65
|
+
}.freeze
|
66
|
+
|
67
|
+
uri = URI.parse(database_url)
|
68
|
+
|
69
|
+
gem_name = supported_databases[uri.scheme]
|
70
|
+
unless gem_name
|
71
|
+
raise "Unsupported database adapter: #{uri.scheme}. Supported adapters: #{supported_databases.keys.join(', ')}"
|
72
|
+
end
|
73
|
+
|
74
|
+
begin
|
75
|
+
require gem_name
|
76
|
+
rescue LoadError
|
77
|
+
raise "Missing required gem for #{uri.scheme}. Please add `gem '#{gem_name}'` to your Gemfile."
|
78
|
+
end
|
79
|
+
|
80
|
+
begin
|
81
|
+
ActiveRecord::Base.establish_connection(database_url)
|
82
|
+
unless ActiveRecord::Base.connection.table_exists?(model.tableize)
|
83
|
+
puts 'Create Transaction Table'
|
84
|
+
ActiveRecord::Base.connection.create_table model.tableize do |t|
|
85
|
+
t.string :gateway
|
86
|
+
t.string :amount
|
87
|
+
t.string :unit
|
88
|
+
t.string :track_id
|
89
|
+
t.string :local_id
|
90
|
+
t.string :ip
|
91
|
+
t.integer :user_id
|
92
|
+
t.integer :cart_id
|
93
|
+
t.text :description
|
94
|
+
t.string :callback_url
|
95
|
+
t.text :gateway_verify_response
|
96
|
+
t.text :gateway_response
|
97
|
+
t.string :status
|
98
|
+
t.timestamps
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
unless Object.const_defined?(model)
|
103
|
+
Object.const_set(model, Class.new(ActiveRecord::Base) do
|
104
|
+
self.table_name = model.tableize
|
105
|
+
end)
|
106
|
+
end
|
107
|
+
|
108
|
+
rescue => e
|
109
|
+
raise "Failed to connect to the database: #{e.message}"
|
110
|
+
end
|
121
111
|
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.redirect_to_gateway(args = {})
|
115
|
+
bank = args.fetch(:bank, available_gateways_list.keys.sample)
|
122
116
|
selected_bank = available_gateways_list.select { |k| k == bank }
|
123
|
-
|
117
|
+
unless selected_bank.present?
|
118
|
+
raise "Bank not enabled or not exists in #{Parsbank.configuration.secrets_path}: #{bank}"
|
119
|
+
end
|
120
|
+
|
121
|
+
description = args.fetch(:description)
|
122
|
+
default_callback = "#{selected_bank[bank.to_s]['callback_url'] || Parsbank.configuration.callback_url}&bank_name=#{bank}"
|
124
123
|
|
125
|
-
|
124
|
+
crypto_amount = args.fetch(:crypto_amount, nil)
|
125
|
+
fiat_amount = args.fetch(:fiat_amount, nil)
|
126
|
+
real_amount = args.fetch(:real_amount, nil)
|
127
|
+
|
128
|
+
if crypto_amount.nil? && fiat_amount.nil? && real_amount.nil?
|
129
|
+
raise 'Amount fileds is emptey: crypto_amount OR fiat_amount OR real_amount'
|
130
|
+
end
|
131
|
+
|
132
|
+
if $SUPPORTED_PSP[bank]['tags'].include?('crypto') && crypto_amount.nil? && real_amount.nil?
|
133
|
+
raise "#{bank} needs crypto_amount or real_amount"
|
134
|
+
end
|
135
|
+
|
136
|
+
if $SUPPORTED_PSP[bank]['tags'].include?('rial') && fiat_amount.nil? && real_amount.nil?
|
137
|
+
raise "#{bank} needs fiat_amount or real_amount"
|
138
|
+
end
|
139
|
+
|
140
|
+
transaction = Object.const_get(Parsbank.configuration.model).create(
|
141
|
+
description: description,
|
142
|
+
amount: fiat_amount || crypto_amount,
|
143
|
+
gateway: bank,
|
144
|
+
callback_url: default_callback,
|
145
|
+
status: 'start',
|
146
|
+
user_id: args.fetch(:user_id, nil),
|
147
|
+
cart_id: args.fetch(:cart_id, nil),
|
148
|
+
local_id: args.fetch(:local_id, nil),
|
149
|
+
ip: args.fetch(:ip, nil)
|
150
|
+
) if Parsbank.configuration.database_url.present?
|
126
151
|
|
127
152
|
case bank
|
128
153
|
when 'mellat'
|
129
154
|
mellat_klass = Parsbank::Mellat.new(
|
130
|
-
amount:
|
155
|
+
amount: fiat_amount,
|
131
156
|
additional_data: description,
|
132
|
-
callback_url:
|
133
|
-
orderId:
|
157
|
+
callback_url: default_callback,
|
158
|
+
orderId: transaction.id
|
134
159
|
)
|
135
160
|
mellat_klass.call
|
161
|
+
transaction.update!(gateway_response: mellat_klass.response, unit: 'irr')
|
136
162
|
result = mellat_klass.redirect_form
|
137
163
|
|
138
164
|
when 'zarinpal'
|
139
165
|
zarinpal_klass = Parsbank::Zarinpal.new(
|
140
|
-
amount:
|
166
|
+
amount: fiat_amount,
|
141
167
|
additional_data: description,
|
142
|
-
callback_url:
|
168
|
+
callback_url: default_callback
|
143
169
|
)
|
144
170
|
zarinpal_klass.call
|
171
|
+
transaction.update!(gateway_response: zarinpal_klass.response,track_id: zarinpal_klass.ref_id, unit: 'irt') if transaction.present?
|
145
172
|
result = zarinpal_klass.redirect_form
|
146
173
|
|
147
174
|
when 'zibal'
|
148
175
|
Parsbank::Zibal.new(
|
149
|
-
amount:
|
176
|
+
amount: fiat_amount,
|
150
177
|
additional_data: description,
|
151
|
-
callback_url:
|
178
|
+
callback_url: default_callback
|
152
179
|
)
|
153
180
|
zarinpal_klass.call
|
154
181
|
result = zarinpal_klass.redirect_form
|
@@ -156,7 +183,10 @@ module Parsbank
|
|
156
183
|
bscbitcoin_klass = Parsbank::BscBitcoin.new(
|
157
184
|
additional_data: description
|
158
185
|
)
|
186
|
+
convert_real_amount_to_assets if crypto_amount.nil? && args.key?(:real_amount)
|
159
187
|
result = bscbitcoin_klass.generate_payment_address(amount: amount)
|
188
|
+
transaction.update!(gateway_response: result, unit: 'bitcoin') if transaction.present?
|
189
|
+
|
160
190
|
end
|
161
191
|
|
162
192
|
result
|
@@ -170,10 +200,10 @@ module Parsbank
|
|
170
200
|
raise "Error: Invalid format in #{Parsbank.configuration.secrets_path}. Expected a hash of bank secrets."
|
171
201
|
end
|
172
202
|
|
173
|
-
supported_banks = $SUPPORTED_PSP
|
203
|
+
supported_banks = $SUPPORTED_PSP.keys
|
174
204
|
|
175
205
|
secrets.each_key do |bank_key|
|
176
|
-
unless supported_banks.include?(bank_key.
|
206
|
+
unless supported_banks.include?(bank_key.to_s)
|
177
207
|
raise "#{bank_key.capitalize} in #{Parsbank.configuration.secrets_path} is not supported by ParsBank. \nSupported Banks: #{supported_banks.join(', ')}"
|
178
208
|
end
|
179
209
|
end
|
@@ -185,23 +215,32 @@ module Parsbank
|
|
185
215
|
raise "Error: YAML syntax issue in #{Parsbank.configuration.secrets_path}: #{e.message}"
|
186
216
|
end
|
187
217
|
|
188
|
-
|
189
218
|
def self.gateways_list_shortcode
|
190
219
|
banks_list = available_gateways_list.keys.map { |bank| render_bank_list_item(bank) }.join
|
191
220
|
"<ul class='parsbank_selector'>#{banks_list}</ul>"
|
192
221
|
end
|
193
222
|
|
194
|
-
private
|
195
223
|
def self.render_bank_list_item(bank)
|
196
|
-
bank_klass=Object.const_get("Parsbank::#{bank.capitalize}")
|
197
|
-
|
224
|
+
bank_klass = Object.const_get("Parsbank::#{bank.capitalize}")
|
225
|
+
_, _, body = begin
|
226
|
+
bank_klass.logo
|
227
|
+
rescue StandardError
|
228
|
+
nil
|
229
|
+
end
|
198
230
|
<<~HTML
|
199
231
|
<li class='parsbank_radio_wrapper #{bank}_wrapper'>
|
200
|
-
|
232
|
+
#{' '}
|
201
233
|
<input type='radio' id='#{bank}' name='bank' value='#{bank}' />
|
202
|
-
<label for='#{bank}'>#{
|
234
|
+
<label for='#{bank}'>#{begin
|
235
|
+
File.read(body)
|
236
|
+
rescue StandardError
|
237
|
+
''
|
238
|
+
end} #{bank.upcase}</label>
|
203
239
|
</li>
|
204
240
|
HTML
|
205
241
|
end
|
206
|
-
|
207
242
|
end
|
243
|
+
|
244
|
+
|
245
|
+
|
246
|
+
Parsbank.initialize_in_rails
|
data/lib/psp.json
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
{
|
2
|
+
"asanpardakht": {
|
3
|
+
"name": "Asan Pardakht CO.",
|
4
|
+
"website": "http://asanpardakht.ir",
|
5
|
+
"tags": ["iranian-psp", "ir", "rial"]
|
6
|
+
},
|
7
|
+
"damavand": {
|
8
|
+
"name": "Electronic Card Damavand CO.",
|
9
|
+
"website": "http://ecd-co.ir",
|
10
|
+
"tags": ["iranian-psp", "ir", "rial"]
|
11
|
+
},
|
12
|
+
"mellat": {
|
13
|
+
"name": "Behpardakht Mellat CO.",
|
14
|
+
"website": "http://behpardakht.com",
|
15
|
+
"tags": ["iranian-psp", "ir", "rial"]
|
16
|
+
},
|
17
|
+
"pep": {
|
18
|
+
"name": "Pasargad CO.",
|
19
|
+
"website": "http://pep.co.ir",
|
20
|
+
"tags": ["iranian-psp", "ir", "rial"]
|
21
|
+
},
|
22
|
+
"sep": {
|
23
|
+
"name": "Saman Bank CO.",
|
24
|
+
"website": "http://sep.ir",
|
25
|
+
"tags": ["iranian-psp", "ir", "rial"]
|
26
|
+
},
|
27
|
+
"pna": {
|
28
|
+
"name": "Pardakht Novin Arian CO.",
|
29
|
+
"website": "http://pna.co.ir",
|
30
|
+
"tags": ["iranian-psp", "ir", "rial"]
|
31
|
+
},
|
32
|
+
"pec": {
|
33
|
+
"name": "Parsian Bank CO.",
|
34
|
+
"website": "http://pec.ir",
|
35
|
+
"tags": ["iranian-psp", "ir", "rial"]
|
36
|
+
},
|
37
|
+
"sadad": {
|
38
|
+
"name": "Sadad Bank CO.",
|
39
|
+
"website": "http://sadadco.com",
|
40
|
+
"tags": ["iranian-psp", "ir", "rial"]
|
41
|
+
},
|
42
|
+
"sayan": {
|
43
|
+
"name": "Sayan Card CO.",
|
44
|
+
"website": "http://sayancard.ir",
|
45
|
+
"tags": ["iranian-psp", "ir", "rial"]
|
46
|
+
},
|
47
|
+
"fanava": {
|
48
|
+
"name": "Fan Ava Card CO.",
|
49
|
+
"website": "http://fanavacard.com",
|
50
|
+
"tags": ["iranian-psp", "ir", "rial"]
|
51
|
+
},
|
52
|
+
"kiccc": {
|
53
|
+
"name": "IranKish CO.",
|
54
|
+
"website": "http://kiccc.com",
|
55
|
+
"tags": ["iranian-psp", "ir", "rial"]
|
56
|
+
},
|
57
|
+
"sepehr": {
|
58
|
+
"name": "Sepehr Bank CO.",
|
59
|
+
"website": "http://www.sepehrpay.com",
|
60
|
+
"tags": ["iranian-psp", "ir", "rial"]
|
61
|
+
},
|
62
|
+
"zarinpal": {
|
63
|
+
"name": "Zarinpal",
|
64
|
+
"website": "http://www.sepehrpay.com",
|
65
|
+
"tags": ["iranian-psp", "irt", "rial"]
|
66
|
+
},
|
67
|
+
"zibal": {
|
68
|
+
"name": "Zibal",
|
69
|
+
"website": "http://zibal.ir",
|
70
|
+
"tags": ["iranian-psp", "irt", "rial"]
|
71
|
+
},
|
72
|
+
"bscbitcoin": {
|
73
|
+
"name": "Binance Bitcoin",
|
74
|
+
"website": "https://bitcoin.org",
|
75
|
+
"tags": ["btc", "bitcoin", "binance", "bsc", "crypto"]
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parsbank
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mohammad Mahmoodi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-01-
|
11
|
+
date: 2025-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -24,34 +24,6 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: faraday
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: faraday_middleware
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
27
|
- !ruby/object:Gem::Dependency
|
56
28
|
name: savon
|
57
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,7 +38,7 @@ dependencies:
|
|
66
38
|
- - ">="
|
67
39
|
- !ruby/object:Gem::Version
|
68
40
|
version: '0'
|
69
|
-
description: Focus on your ecommerce we handle your
|
41
|
+
description: Focus on your ecommerce we handle your gateways.
|
70
42
|
email:
|
71
43
|
- mm580486@gmail.com
|
72
44
|
executables: []
|
@@ -88,6 +60,7 @@ files:
|
|
88
60
|
- lib/parsbank/zarinpal/zarinpal.rb
|
89
61
|
- lib/parsbank/zibal/logo.svg
|
90
62
|
- lib/parsbank/zibal/zibal.rb
|
63
|
+
- lib/psp.json
|
91
64
|
- sig/parsbank.rbs
|
92
65
|
homepage: https://github.com/Abrfanet
|
93
66
|
licenses:
|