zarinpal 0.0.1
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 +7 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Guardfile +24 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/doc/Zarinpal.html +338 -0
- data/doc/Zarinpal/Configuration.html +445 -0
- data/doc/Zarinpal/Errors.html +149 -0
- data/doc/Zarinpal/PaymentRequest.html +805 -0
- data/doc/Zarinpal/PaymentVerification.html +540 -0
- data/doc/Zarinpal/Response.html +669 -0
- data/doc/Zarinpal/Response/ResponseError.html +123 -0
- data/doc/_index.html +184 -0
- data/doc/class_list.html +54 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +338 -0
- data/doc/doc/_index.html +88 -0
- data/doc/doc/class_list.html +54 -0
- data/doc/doc/css/common.css +1 -0
- data/doc/doc/css/full_list.css +57 -0
- data/doc/doc/css/style.css +338 -0
- data/doc/doc/file_list.html +53 -0
- data/doc/doc/frames.html +26 -0
- data/doc/doc/index.html +88 -0
- data/doc/doc/js/app.js +214 -0
- data/doc/doc/js/full_list.js +178 -0
- data/doc/doc/js/jquery.js +4 -0
- data/doc/doc/method_list.html +53 -0
- data/doc/doc/top-level-namespace.html +102 -0
- data/doc/file.README.html +105 -0
- data/doc/file_list.html +56 -0
- data/doc/frames.html +26 -0
- data/doc/index.html +105 -0
- data/doc/js/app.js +214 -0
- data/doc/js/full_list.js +178 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +191 -0
- data/doc/top-level-namespace.html +112 -0
- data/lib/zarinpal.rb +37 -0
- data/lib/zarinpal/errors.rb +17 -0
- data/lib/zarinpal/payment_request.rb +44 -0
- data/lib/zarinpal/payment_verification.rb +34 -0
- data/lib/zarinpal/response.rb +50 -0
- data/lib/zarinpal/version.rb +3 -0
- data/spec/lib/configuration_spec.rb +18 -0
- data/spec/lib/errors_spec.rb +7 -0
- data/spec/lib/payment_request_spec.rb +40 -0
- data/spec/lib/payment_verification_spec.rb +41 -0
- data/spec/lib/response_spec.rb +46 -0
- data/spec/lib/version_spec.rb +7 -0
- data/spec/spec_helper.rb +20 -0
- data/zarinpal.gemspec +29 -0
- metadata +206 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
+
<head>
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
6
|
+
<title>
|
7
|
+
Top Level Namespace
|
8
|
+
|
9
|
+
— Documentation by YARD 0.8.7.2
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
|
16
|
+
|
17
|
+
<script type="text/javascript" charset="utf-8">
|
18
|
+
hasFrames = window.top.frames.main ? true : false;
|
19
|
+
relpath = '';
|
20
|
+
framesUrl = "frames.html#!" + escape(window.location.href);
|
21
|
+
</script>
|
22
|
+
|
23
|
+
|
24
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
25
|
+
|
26
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
27
|
+
|
28
|
+
|
29
|
+
</head>
|
30
|
+
<body>
|
31
|
+
<div id="header">
|
32
|
+
<div id="menu">
|
33
|
+
|
34
|
+
<a href="_index.html">Index</a> »
|
35
|
+
|
36
|
+
|
37
|
+
<span class="title">Top Level Namespace</span>
|
38
|
+
|
39
|
+
|
40
|
+
<div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<div id="search">
|
44
|
+
|
45
|
+
<a class="full_list_link" id="class_list_link"
|
46
|
+
href="class_list.html">
|
47
|
+
Class List
|
48
|
+
</a>
|
49
|
+
|
50
|
+
<a class="full_list_link" id="method_list_link"
|
51
|
+
href="method_list.html">
|
52
|
+
Method List
|
53
|
+
</a>
|
54
|
+
|
55
|
+
<a class="full_list_link" id="file_list_link"
|
56
|
+
href="file_list.html">
|
57
|
+
File List
|
58
|
+
</a>
|
59
|
+
|
60
|
+
</div>
|
61
|
+
<div class="clear"></div>
|
62
|
+
</div>
|
63
|
+
|
64
|
+
<iframe id="search_frame"></iframe>
|
65
|
+
|
66
|
+
<div id="content"><h1>Top Level Namespace
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
</h1>
|
71
|
+
|
72
|
+
<dl class="box">
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
</dl>
|
82
|
+
<div class="clear"></div>
|
83
|
+
|
84
|
+
<h2>Defined Under Namespace</h2>
|
85
|
+
<p class="children">
|
86
|
+
|
87
|
+
|
88
|
+
<strong class="modules">Modules:</strong> <span class='object_link'><a href="Zarinpal.html" title="Zarinpal (module)">Zarinpal</a></span>
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
</p>
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
</div>
|
104
|
+
|
105
|
+
<div id="footer">
|
106
|
+
Generated on Sat Mar 1 21:03:58 2014 by
|
107
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
108
|
+
0.8.7.2 (ruby-2.1.0).
|
109
|
+
</div>
|
110
|
+
|
111
|
+
</body>
|
112
|
+
</html>
|
data/lib/zarinpal.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'zarinpal/version'
|
2
|
+
require 'zarinpal/payment_request'
|
3
|
+
require 'zarinpal/response'
|
4
|
+
require 'zarinpal/payment_verification'
|
5
|
+
require 'zarinpal/errors'
|
6
|
+
|
7
|
+
# A library to send and verify transactions with Zaripal
|
8
|
+
#
|
9
|
+
# @see http://zarinpal.com/
|
10
|
+
# @author {http://github.com/arashm Arash Mousavi}
|
11
|
+
module Zarinpal
|
12
|
+
class << self
|
13
|
+
attr_accessor :configuration
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configure
|
17
|
+
self.configuration ||= Configuration.new
|
18
|
+
yield configuration
|
19
|
+
end
|
20
|
+
|
21
|
+
# Configures the gem
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# Zarinpal.configure do |config|
|
25
|
+
# config.merchant_id = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
|
26
|
+
# config.callback_url = 'http://example.com/call_back'
|
27
|
+
# config.client = 'https://de.zarinpal.com/pg/services/WebGate/wsdl'
|
28
|
+
# end
|
29
|
+
class Configuration
|
30
|
+
attr_accessor :merchant_id, :callback_url, :client
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@client = 'https://de.zarinpal.com/pg/services/WebGate/wsdl'
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Zarinpal
|
2
|
+
# List of status code errors and description
|
3
|
+
module Errors
|
4
|
+
# List of status code errors and description
|
5
|
+
IDS = {
|
6
|
+
'-1' => 'Insufficient information',
|
7
|
+
'-2' => 'IP or Merchant Code is not correct',
|
8
|
+
'-3' => 'Amount should be greater than 1000',
|
9
|
+
'-4' => 'Insufficient',
|
10
|
+
'-11' => 'Requested response didn\'t find',
|
11
|
+
'-21' => 'No financial action found for this transaction',
|
12
|
+
'-22' => 'Unsuccessful transaction',
|
13
|
+
'-33' => 'Transaction price is not equal to payed amount',
|
14
|
+
'-54' => 'The request had archived'
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "savon"
|
2
|
+
|
3
|
+
module Zarinpal
|
4
|
+
# Sends a payment request to zarinpal
|
5
|
+
# @return [Zarinpal::Response]
|
6
|
+
class PaymentRequest
|
7
|
+
attr_accessor :amount, :description, :email, :mobile
|
8
|
+
attr_reader :response
|
9
|
+
|
10
|
+
# @note A hash of parameters should be send to this class
|
11
|
+
# @example
|
12
|
+
# PaymentRequest.new(amount: 10000, description: 'sth...', email: 'example@example.com')
|
13
|
+
#
|
14
|
+
# @param args [Hash] hash of params to send requests
|
15
|
+
# @option args [Integer] :amount price of the request
|
16
|
+
# @option args [Integer] :description description of transaction
|
17
|
+
# @option args [String] :email ('') email of buyer
|
18
|
+
# @option args [String] :mobile ('') mobile number of buyer
|
19
|
+
def initialize(args = {})
|
20
|
+
@amount = args.fetch(:amount)
|
21
|
+
@description = args.fetch(:description)
|
22
|
+
@email = args.fetch(:email, '')
|
23
|
+
@mobile = args.fetch(:mobile, '')
|
24
|
+
@client = Savon.client(wsdl: Zarinpal.configuration.client, pretty_print_xml: true)
|
25
|
+
@response = Response.new
|
26
|
+
end
|
27
|
+
|
28
|
+
# Sends the payment request to Zarinpal
|
29
|
+
#
|
30
|
+
# @return [Zarinpal::Response]
|
31
|
+
def call
|
32
|
+
response = @client.call :payment_request, message: {
|
33
|
+
'MerchantID' => Zarinpal.configuration.merchant_id,
|
34
|
+
'Amount' => @amount,
|
35
|
+
'Description' => @description,
|
36
|
+
'Email' => @email,
|
37
|
+
'Mobile' => @mobile,
|
38
|
+
'CallbackURL' => Zarinpal.configuration.callback_url
|
39
|
+
}
|
40
|
+
|
41
|
+
@response.validate(response.body)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "savon"
|
2
|
+
|
3
|
+
module Zarinpal
|
4
|
+
# Verifyes transaction with Zarinpal
|
5
|
+
class PaymentVerification
|
6
|
+
attr_reader :status, :refid
|
7
|
+
|
8
|
+
# @note A hash of parameters should be send to this class
|
9
|
+
# @example
|
10
|
+
# PaymentVerification.new(authority: 'xxx-xxx', amount: 10000)
|
11
|
+
#
|
12
|
+
# @param args [Hash] hash of params to verify transaction
|
13
|
+
# @option args [String] :authority Authority code returned from PaymentRequest
|
14
|
+
# @option args [Integer] :amount price of the request
|
15
|
+
def initialize(args = {})
|
16
|
+
@authority = args.fetch(:authority)
|
17
|
+
@amount = args.fetch(:amount)
|
18
|
+
@client = Savon.client(wsdl: Zarinpal.configuration.client, pretty_print_xml: true)
|
19
|
+
@response = Response.new
|
20
|
+
end
|
21
|
+
|
22
|
+
# Send verification request to Zarinpal
|
23
|
+
#
|
24
|
+
# @return [Zarinpal::Response]
|
25
|
+
def verify
|
26
|
+
response = @client.call :payment_verification, message: {
|
27
|
+
'MerchantID' => Zarinpal.configuration.merchant_id,
|
28
|
+
'Authority' => @authority,
|
29
|
+
'Amount' => @amount
|
30
|
+
}
|
31
|
+
@response.validate(response.body)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Zarinpal
|
2
|
+
# This classs manages the returned response from PaymentVerification and
|
3
|
+
# PaymentRequest. If the response is not valid (the status is not code is 100 or 101)
|
4
|
+
# it will raise an error with the corresponding description.
|
5
|
+
class Response
|
6
|
+
|
7
|
+
class ResponseError < RuntimeError; end
|
8
|
+
|
9
|
+
attr_reader :response, :authority, :status, :refid
|
10
|
+
|
11
|
+
# Checks if the transaction response returned from PaymentRequest
|
12
|
+
# or PaymentVerification is valid
|
13
|
+
#
|
14
|
+
# @param [#response Hash]
|
15
|
+
# @raise [ArgumentError] if response is nil
|
16
|
+
# @raise [ResponseError] if response is not valid
|
17
|
+
# @return [Response]
|
18
|
+
def validate(response = nil)
|
19
|
+
@response = response
|
20
|
+
perform_validation
|
21
|
+
|
22
|
+
return self
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the validation status of response
|
26
|
+
#
|
27
|
+
# @return [boolean]
|
28
|
+
def valid?
|
29
|
+
@valid
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def perform_validation
|
34
|
+
raise ArgumentError, 'not a valid response' if @response.nil?
|
35
|
+
|
36
|
+
body = @response[:payment_request_response] || @response[:payment_verification_response]
|
37
|
+
@authority = body[:authority]
|
38
|
+
@status = body[:status].to_i
|
39
|
+
@refid = body[:ref_id]
|
40
|
+
|
41
|
+
if not ['100', '101'].include?(body[:status])
|
42
|
+
@valid = false
|
43
|
+
raise ResponseError, Errors::IDS[body[:status]]
|
44
|
+
else
|
45
|
+
@valid = true
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zarinpal do
|
4
|
+
before do
|
5
|
+
Zarinpal.configure do |config|
|
6
|
+
config.merchant_id = '52fbc7f4-3ca4-4b40-88ee-287f5ee8a9d4'
|
7
|
+
config.callback_url = 'http://www.m0b.ir/verify.php'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'responses to merchant_id' do
|
12
|
+
expect(Zarinpal.configuration.merchant_id).to eq('52fbc7f4-3ca4-4b40-88ee-287f5ee8a9d4')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'responses to client with default value' do
|
16
|
+
expect(Zarinpal.configuration.client).to eq('https://de.zarinpal.com/pg/services/WebGate/wsdl')
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zarinpal::PaymentRequest do
|
4
|
+
include Savon::SpecHelper
|
5
|
+
|
6
|
+
let(:zarin) {
|
7
|
+
Zarinpal::PaymentRequest.new(
|
8
|
+
{
|
9
|
+
amount: 10000,
|
10
|
+
description: 'sth for test'
|
11
|
+
})
|
12
|
+
}
|
13
|
+
|
14
|
+
let(:uri) { 'https://de.zarinpal.com/pg/services/WebGate/wsdl' }
|
15
|
+
|
16
|
+
before(:all) do
|
17
|
+
savon.mock!
|
18
|
+
|
19
|
+
Zarinpal.configure do |config|
|
20
|
+
config.merchant_id = '52fbc7f4-3ca4-4b40-88ee-287f5ee8a9d4'
|
21
|
+
config.callback_url = 'http://example.com/callback_url'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
after(:all) { savon.unmock! }
|
26
|
+
|
27
|
+
it 'responses to call' do
|
28
|
+
expect(zarin).to respond_to(:call)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'successfully calls the server' do
|
32
|
+
message = {"MerchantID"=>"52fbc7f4-3ca4-4b40-88ee-287f5ee8a9d4", "Amount"=>10000, "Description"=>"sth for test", "Email"=>"", "Mobile"=>"", "CallbackURL"=>"http://example.com/callback_url"}
|
33
|
+
|
34
|
+
savon.expects(:payment_request).with(message: message ).returns('<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://zarinpal.com/"><SOAP-ENV:Body><ns1:PaymentRequestResponse><ns1:Status>101</ns1:Status><ns1:Authority>"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"</ns1:Authority></ns1:PaymentRequestResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>')
|
35
|
+
|
36
|
+
response = zarin.call
|
37
|
+
expect(response).to be_valid
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zarinpal::PaymentVerification, focus: true do
|
4
|
+
include Savon::SpecHelper
|
5
|
+
|
6
|
+
let(:pv) {
|
7
|
+
Zarinpal::PaymentVerification.new(
|
8
|
+
{
|
9
|
+
authority: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
|
10
|
+
amount: 10000
|
11
|
+
})
|
12
|
+
}
|
13
|
+
|
14
|
+
before(:all) do
|
15
|
+
savon.mock!
|
16
|
+
|
17
|
+
Zarinpal.configure do |config|
|
18
|
+
config.merchant_id = '52fbc7f4-3ca4-4b40-88ee-287f5ee8a9d4'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
after(:all) { savon.unmock! }
|
23
|
+
|
24
|
+
it 'successfully verify the request' do
|
25
|
+
message = {"MerchantID"=>"52fbc7f4-3ca4-4b40-88ee-287f5ee8a9d4", "Authority"=>'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', "Amount"=>10000}
|
26
|
+
|
27
|
+
savon.expects(:payment_verification).with(message: message ).returns('<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://zarinpal.com/">
|
28
|
+
<SOAP-ENV:Body>
|
29
|
+
<ns1:PaymentVerificationResponse>
|
30
|
+
<ns1:Status>100</ns1:Status>
|
31
|
+
<ns1:RefID>22xxff33</ns1:RefID>
|
32
|
+
</ns1:PaymentVerificationResponse>
|
33
|
+
</SOAP-ENV:Body>
|
34
|
+
</SOAP-ENV:Envelope>')
|
35
|
+
|
36
|
+
response = pv.verify
|
37
|
+
expect(response).to be_valid
|
38
|
+
expect(response.refid).to eq('22xxff33')
|
39
|
+
expect(response.status).to eq(100)
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zarinpal::Response do
|
4
|
+
let(:response) { Zarinpal::Response.new }
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
Zarinpal.configure do |config|
|
8
|
+
config.merchant_id = '52fbc7f4-3ca4-4b40-88ee-287f5ee8a9d4'
|
9
|
+
config.callback_url = 'http://example.com/callback_url'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'responses to authority and status' do
|
14
|
+
expect(response).to respond_to :authority
|
15
|
+
expect(response).to respond_to :status
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'Validation' do
|
19
|
+
it 'fails validation if response is nil' do
|
20
|
+
zarin = Zarinpal::Response.new
|
21
|
+
expect{ zarin.validate }.to raise_error(ArgumentError, 'not a valid response')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'fails if status is less than 0' do
|
25
|
+
response = { payment_request_response: {status: "-2" } }
|
26
|
+
zarin = Zarinpal::Response.new
|
27
|
+
expect { zarin.validate(response) }.to raise_error(Zarinpal::Response::ResponseError, 'IP or Merchant Code is not correct')
|
28
|
+
expect(zarin).to_not be_valid
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'fails if authority is less than 36 character' do
|
32
|
+
response = { payment_request_response: {authority: "this-is-wrong"} }
|
33
|
+
zarin = Zarinpal::Response.new.validate response
|
34
|
+
expect(zarin).to_not be_valid
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'is successful' do
|
38
|
+
response = { payment_request_response: {authority: "x" * 36, status: 100 } }
|
39
|
+
zarin = Zarinpal::Response.new.validate response
|
40
|
+
|
41
|
+
expect(zarin).to be_valid
|
42
|
+
expect(zarin.authority).to eq("x" * 36)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|