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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 99f934eb7c7f86c5672690ea579ec6e1e5839500ec0c75268a49a1878bc50973
4
- data.tar.gz: be36aa6271f70befb1ab12486bfa996757032807bd3ca39a43df2cb47dc7efbe
3
+ metadata.gz: b9f5826abb8b78b831b9be8e8565e004863a75b7c64ff44beabd899a93bbafa4
4
+ data.tar.gz: 2c6ad504a2e26757a781c144844843f224da7ee57d80346d09dd74b4b08acac4
5
5
  SHA512:
6
- metadata.gz: 10d5222300d787982406ac0d8f260609f3fc6aa14ab8f2dc7317be84755c70cf79e49aab330dfc569ab970636c42efec1e3cc60d5f57308274db123b07f1933d
7
- data.tar.gz: 03d92ce711d799e2d8cb268c8fb9c176926cc69eadc6af4030c1730fe50ba0c42bbefb0dddcdac46a7ce39f7ab971c8284fddc8703e650467eb98883551dd7b3
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
- ParsBank Gateway
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
- Install the gem and add to the application's Gemfile by executing:
13
+ ### Using Bundler
14
+ Add ParsBank to your application's Gemfile and install it with:
15
15
 
16
- $ bundle add ParsBank
16
+ ```bash
17
+ $ bundle add ParsBank
18
+ ```
17
19
 
18
- If bundler is not being used to manage dependencies, install the gem by executing:
20
+ ### Without Bundler
21
+ Install the gem directly:
19
22
 
20
- $ gem install ParsBank
23
+ ```bash
24
+ $ gem install ParsBank
25
+ ```
21
26
 
22
- In sinatra application just add `gem "ParsBank"` on your Gemfile and install with `bundle install`
27
+ ### For Sinatra Applications
28
+ Include the gem in your Gemfile:
23
29
 
24
- ## Usage
30
+ ```ruby
31
+ gem "ParsBank"
32
+ ```
25
33
 
26
- First step:
34
+ Then install it with:
27
35
 
28
- in config/initilizers create new config file:
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
- config.callback_url = 'YOUR CALLBACK LOCATION LIKE https://example.com/CallBack'
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
- config.sandbox = false # Enable Simulation for your requst also approve callback without verification
112
+ end
113
+ ```
40
114
 
41
- config.webhook = "https://yoursite.com/income-webhook?title=TITLE&message=MESSAGE" # Webhook for notify any success transactions or errors on cominiucate with Core Bank
42
- config.webhook_method = 'GET' # or POST
115
+ ---
43
116
 
44
- config.mitm_server = 'YOUR_MITM_SERVER_LOCATION as HTTP or HTTPS'
117
+ ## ParsBank Amazing Web
45
118
 
46
- config.secrets_path = Rails.root.join('config/bank_secrets.yaml') #PATH OF YOUR BANKS CREDITS like merchant id, username, password or token
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
- config.min_amount = '10000' # as rials
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
- # WebPanel Config
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
- # Secure by captcha
61
- config.captcha = false
128
+ ### Setup for ParsBank Web
62
129
 
63
- # Model for store transactions
64
- # Transaction model should have amount, status, bank_name, callback_url, authority_code or anything you need
65
- config.model = Transaction
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
- end
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
- Inside of your controller call Token action and get url for redirect user to Gateway page
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
- Get Ready For ParsBank Web Gem:
169
+ ## 🚀 Premium Features
94
170
 
95
- ## Method 1 (Isolated Dockerfile)
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
- ## Method 2 (Inside of Rails App)
181
+ #### 💳 USDT Wallet Addresses
182
+ - **USDT (ERC-20):** `0x2028f409a42413076665231e209896bbe0221d64`
183
+ - **USDT (TRC-20):** `TNtLkdy2FAKKBGJ4F8ij2mppWpjkB8GULy`
104
184
 
105
- ## Development
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
- We don't accept any pull request, just use issue section
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 pull requests are welcome on GitHub at https://github.com/[USERNAME]/ParsBank.
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
@@ -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
- Rails.logger.info("Received response with status: #{response.status}, body: #{response.body.inspect}")
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 Faraday::ConnectionFailed => e
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 webhook(_message)
37
- webhook_url = Parsbank.configuration.webhook
38
-
39
- webhook_url.gsub!('MESSAGE', _message) if Parsbank.configuration.webhook_method == :get
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
- when :get
60
- connection.get('&parsbank')
61
- end
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 setup_connection
71
- @connection = Faraday.new(@endpoint) do |conn|
72
- conn.request :json if @response_type == :json # Automatically converts payload to JSON
73
- conn.response :json if @response_type == :json # Automatically parses JSON response
74
- conn.adapter Faraday.default_adapter
75
- conn.request :retry, max: 3, interval: 0.05,
76
- interval_randomness: 0.5, backoff_factor: 2,
77
- exceptions: [Faraday::TimeoutError, Faraday::ConnectionFailed]
78
- conn.use FaradayMiddleware::FollowRedirects
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 send_request
83
- headers = default_headers.merge(@headers)
84
-
77
+ def build_request
85
78
  case @http_method
86
79
  when :post
87
- perform_post(headers)
80
+ build_post_request
88
81
  when :get
89
- perform_get(headers)
82
+ build_get_request
90
83
  when :options
91
- perform_options(headers)
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 perform_post(headers)
98
- @connection.post(@action) do |req|
99
- req.headers = headers
100
- req.body = @request_message
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 perform_get(headers)
105
- @connection.get(@action) do |req|
106
- req.headers = headers
107
- req.params = @request_message
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 perform_options(headers)
112
- @connection.options(@action) do |req|
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
- Rails.logger.error("Request to #{@endpoint}/#{@action} failed with status: #{response.status}, error: #{response.body.inspect}")
126
- raise "API request failed with status #{response.status}: #{response.body}"
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
- Rails.logger.error(message)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parsbank
4
- VERSION = '0.0.4'
4
+ VERSION = '0.0.5'
5
5
  end
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
- 'asanpardakht': {
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.redirect_to_gateway(args = {})
118
- amount = args.fetch(:amount)
119
- bank = args.fetch(:bank, 'random-irr-gates')
120
- description = args.fetch(:description, '')
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
- raise "Bank not enabled or not exists on bank_secrets.yml: #{bank}" unless selected_bank.present?
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
- default_callback = Parsbank.configuration.callback_url + "&bank_name=#{bank}"
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: amount,
155
+ amount: fiat_amount,
131
156
  additional_data: description,
132
- callback_url: selected_bank['mellat']['callback_url'] || default_callback,
133
- orderId: rand(1...9999)
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: amount,
166
+ amount: fiat_amount,
141
167
  additional_data: description,
142
- callback_url: selected_bank['zarinpal']['callback_url'] || default_callback
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: amount,
176
+ amount: fiat_amount,
150
177
  additional_data: description,
151
- callback_url: selected_bank['zibal']['callback_url'] || default_callback
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[0].keys
203
+ supported_banks = $SUPPORTED_PSP.keys
174
204
 
175
205
  secrets.each_key do |bank_key|
176
- unless supported_banks.include?(bank_key.to_sym)
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
- status, headers, body = bank_klass.logo rescue nil
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}'>#{File.read(body) rescue ''} #{bank.upcase}</label>
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
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-08 00:00:00.000000000 Z
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 payments.
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: