zarinpal 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +24 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +29 -0
  8. data/Rakefile +1 -0
  9. data/doc/Zarinpal.html +338 -0
  10. data/doc/Zarinpal/Configuration.html +445 -0
  11. data/doc/Zarinpal/Errors.html +149 -0
  12. data/doc/Zarinpal/PaymentRequest.html +805 -0
  13. data/doc/Zarinpal/PaymentVerification.html +540 -0
  14. data/doc/Zarinpal/Response.html +669 -0
  15. data/doc/Zarinpal/Response/ResponseError.html +123 -0
  16. data/doc/_index.html +184 -0
  17. data/doc/class_list.html +54 -0
  18. data/doc/css/common.css +1 -0
  19. data/doc/css/full_list.css +57 -0
  20. data/doc/css/style.css +338 -0
  21. data/doc/doc/_index.html +88 -0
  22. data/doc/doc/class_list.html +54 -0
  23. data/doc/doc/css/common.css +1 -0
  24. data/doc/doc/css/full_list.css +57 -0
  25. data/doc/doc/css/style.css +338 -0
  26. data/doc/doc/file_list.html +53 -0
  27. data/doc/doc/frames.html +26 -0
  28. data/doc/doc/index.html +88 -0
  29. data/doc/doc/js/app.js +214 -0
  30. data/doc/doc/js/full_list.js +178 -0
  31. data/doc/doc/js/jquery.js +4 -0
  32. data/doc/doc/method_list.html +53 -0
  33. data/doc/doc/top-level-namespace.html +102 -0
  34. data/doc/file.README.html +105 -0
  35. data/doc/file_list.html +56 -0
  36. data/doc/frames.html +26 -0
  37. data/doc/index.html +105 -0
  38. data/doc/js/app.js +214 -0
  39. data/doc/js/full_list.js +178 -0
  40. data/doc/js/jquery.js +4 -0
  41. data/doc/method_list.html +191 -0
  42. data/doc/top-level-namespace.html +112 -0
  43. data/lib/zarinpal.rb +37 -0
  44. data/lib/zarinpal/errors.rb +17 -0
  45. data/lib/zarinpal/payment_request.rb +44 -0
  46. data/lib/zarinpal/payment_verification.rb +34 -0
  47. data/lib/zarinpal/response.rb +50 -0
  48. data/lib/zarinpal/version.rb +3 -0
  49. data/spec/lib/configuration_spec.rb +18 -0
  50. data/spec/lib/errors_spec.rb +7 -0
  51. data/spec/lib/payment_request_spec.rb +40 -0
  52. data/spec/lib/payment_verification_spec.rb +41 -0
  53. data/spec/lib/response_spec.rb +46 -0
  54. data/spec/lib/version_spec.rb +7 -0
  55. data/spec/spec_helper.rb +20 -0
  56. data/zarinpal.gemspec +29 -0
  57. 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
+ &mdash; 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> &raquo;
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>
@@ -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,3 @@
1
+ module Zarinpal
2
+ VERSION = "0.0.1"
3
+ 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,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zarinpal::Errors do
4
+
5
+ it { respond_to(:IDS) }
6
+
7
+ 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