active_merchant_payumoney_com 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.
@@ -0,0 +1,2 @@
1
+ # please add general patterns to your global ignore list
2
+ # see https://github.com/github/gitignore#readme
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org" unless ENV['QUICK']
2
+ gemspec
data/License ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Ram Singla
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ 'Software'), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,37 @@
1
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
2
+
3
+ begin
4
+ require 'bundler'
5
+ Bundler::GemHelper.install_tasks
6
+ rescue LoadError => e
7
+ $stderr.puts e
8
+ end
9
+
10
+ desc "run specs"
11
+ task(:spec) { ruby '-S rspec spec' }
12
+
13
+ desc "generate gemspec"
14
+ task 'active_merchant_payu_in.gemspec' do
15
+ require 'active_merchant_payu_in/version'
16
+ content = File.read 'active_merchant_payu_in.gemspec'
17
+
18
+ fields = {
19
+ :authors => `git shortlog -sn`.scan(/[^\d\s].*/),
20
+ :email => `git shortlog -sne`.scan(/[^<]+@[^>]+/),
21
+ :files => `git ls-files`.split("\n").reject { |f| f =~ /^(\.|Gemfile)/ }
22
+ }
23
+
24
+ fields.each do |field, values|
25
+ updated = " s.#{field} = ["
26
+ updated << values.map { |v| "\n %p" % v }.join(',')
27
+ updated << "\n ]"
28
+ content.sub!(/ s\.#{field} = \[\n( .*\n)* \]/, updated)
29
+ end
30
+
31
+ content.sub! /(s\.version.*=\s+).*/, "\\1\"#{ActiveMerchantPayuIn::VERSION}\""
32
+ File.open('active_merchant_payu_in.gemspec', 'w') { |f| f << content }
33
+ end
34
+
35
+ task :gemspec => 'active_merchant_payu_in.gemspec'
36
+ task :default => :spec
37
+ task :test => :spec
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "active_merchant/payu_in/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "active_merchant_payumoney_com"
7
+ s.version = ActiveMerchant::Billing::Integrations::PayuIn::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ram Singla"]
10
+ s.email = ["ram.singla@gmail.com"]
11
+ s.homepage = "http://www.payumoney.com"
12
+ s.summary = %q{PayUMoney.com Payment Gateweay Integration Kit}
13
+ s.description = %q{PayUMoney.com Payment Gateway integration for ActiveMerchant}
14
+
15
+ s.rubyforge_project = "active_merchant_payumoney_com"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 3
25
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
26
+ s.add_development_dependency(%q<rspec>, ["~> 2.7.0"])
27
+ s.add_runtime_dependency(%q<activemerchant>, ["~> 1.8"])
28
+ else
29
+ s.add_dependency(%q<rspec>, ["~> 2.7.0"])
30
+ s.add_dependency(%q<activemerchant>, ["~> 1.8"])
31
+ end
32
+ else
33
+ s.add_dependency(%q<rspec>, ["~> 2.7.0"])
34
+ s.add_dependency(%q<activemerchant>, ["~> 1.8"])
35
+ end
36
+ end
@@ -0,0 +1,70 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module PayuIn
5
+ class Helper < ActiveMerchant::Billing::Integrations::Helper
6
+
7
+ mapping :amount, 'amount'
8
+ mapping :account, 'key'
9
+ mapping :order, 'txnid'
10
+ mapping :credential2, 'productinfo'
11
+
12
+ mapping :customer, :first_name => 'firstname',
13
+ :last_name => 'lastname',
14
+ :email => 'email',
15
+ :phone => 'phone'
16
+
17
+ mapping :billing_address, :city => 'city',
18
+ :address1 => 'address1',
19
+ :address2 => 'address2',
20
+ :state => 'state',
21
+ :zip => 'zip',
22
+ :country => 'country'
23
+
24
+ # Which tab you want to be open default on PayU
25
+ # CC (CreditCard) or NB (NetBanking)
26
+ mapping :mode, 'pg'
27
+
28
+ mapping :notify_url, 'notify_url'
29
+ mapping :return_url, :success => 'surl',
30
+ :failure => 'furl'
31
+ mapping :cancel_return_url, 'curl'
32
+ mapping :checksum, 'hash'
33
+
34
+ mapping :user_defined, { :var1 => 'udf1',
35
+ :var2 => 'udf2',
36
+ :var3 => 'udf3',
37
+ :var4 => 'udf4',
38
+ :var5 => 'udf5',
39
+ :var6 => 'udf6',
40
+ :var7 => 'udf7',
41
+ :var8 => 'udf8',
42
+ :var9 => 'udf9',
43
+ :var10 => 'udf10'
44
+ }
45
+
46
+ # Computes the checksum for the form data and add it to the field
47
+ def add_checksum( options = {} )
48
+ checksum_fields = [ :order, :amount, :credential2, { :customer => [ :first_name, :email ] },
49
+ { :user_defined => [ :var1, :var2, :var3, :var4, :var5, :var6, :var7, :var8, :var9, :var10 ] } ]
50
+ checksum_payload_items = checksum_fields.inject( [] ) do | items, field |
51
+ if Hash === field then
52
+ key = field.keys.first
53
+ field[key].inject( items ){ |s,x| items.push( form_fields[ mappings[key][x] ] ) }
54
+ # items.push( form_fields[ field.values.each{ |x| mappings[ key ][ x ] } ] )
55
+ else
56
+ items.push( form_fields[ mappings[field] ] )
57
+ end
58
+ end
59
+ checksum_payload_items.push( options )
60
+ checksum = PayuIn.checksum( *checksum_payload_items )
61
+ add_field( mappings[:checksum], checksum )
62
+ return checksum_payload_items
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,154 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module PayuIn
5
+ class Notification < ActiveMerchant::Billing::Integrations::Notification
6
+
7
+ # Was the transaction complete?
8
+ def complete?
9
+ status == "success"
10
+ end
11
+
12
+ # Status of the transaction. List of possible values:
13
+ # <tt>invalid</tt>:: transaction id is not present
14
+ # <tt>tampered</tt>:: transaction data has been tampered
15
+ # <tt>success</tt>:: transaction successful
16
+ # <tt>pending</tt>:: transaction is pending for some approval
17
+ # <tt>failure</tt>:: transaction failure
18
+ def status
19
+ @status ||= if checksum_ok?
20
+ if transaction_id.blank?
21
+ 'invalid'
22
+ else
23
+ transaction_status.downcase
24
+ end
25
+ else
26
+ 'tampered'
27
+ end.freeze
28
+ end
29
+
30
+ def invoice_ok?( order_id )
31
+ order_id.to_s == invoice.to_s
32
+ end
33
+
34
+ # Order amount should be equal to gross - discount
35
+ def amount_ok?( order_amount, order_discount = BigDecimal.new( '0.0' ) )
36
+ BigDecimal.new( gross ) == order_amount && BigDecimal.new( discount ) == order_discount
37
+ end
38
+
39
+ # Status of transaction return from the PayU. List of possible values:
40
+ # <tt>SUCCESS</tt>::
41
+ # <tt>PENDING</tt>::
42
+ # <tt>FAILURE</tt>::
43
+ def transaction_status
44
+ params['status']
45
+ end
46
+
47
+ # ID of this transaction (PayU.in number)
48
+ def transaction_id
49
+ params['mihpayid']
50
+ end
51
+
52
+ # Mode of Payment
53
+ #
54
+ # 'CC' for credit-card
55
+ # 'NB' for net-banking
56
+ # 'CD' for cheque or DD
57
+ # 'CO' for Cash Pickup
58
+ def type
59
+ params['mode']
60
+ end
61
+
62
+ # What currency have we been dealing with
63
+ def currency
64
+ 'INR'
65
+ end
66
+
67
+ # This is the invoice which you passed to PayU.in
68
+ def invoice
69
+ params['txnid']
70
+ end
71
+
72
+ # Merchant Id provided by the PayU.in
73
+ def account
74
+ params['key']
75
+ end
76
+
77
+ # original amount send by merchant
78
+ def gross
79
+ params['amount']
80
+ end
81
+
82
+ # This is discount given to user - based on promotion set by merchants.
83
+ def discount
84
+ params['discount']
85
+ end
86
+
87
+ # Description offer for what PayU given the offer to user - based on promotion set by merchants.
88
+ def offer_description
89
+ params['offer']
90
+ end
91
+
92
+ # Information about the product as send by merchant
93
+ def product_info
94
+ params['productinfo']
95
+ end
96
+
97
+ # Email of the customer
98
+ def customer_email
99
+ params['email']
100
+ end
101
+
102
+ # Phone of the customer
103
+ def customer_phone
104
+ params['phone']
105
+ end
106
+
107
+ # Firstname of the customer
108
+ def customer_first_name
109
+ params['firstname']
110
+ end
111
+
112
+ # Lastname of the customer
113
+ def customer_last_name
114
+ params['lastname']
115
+ end
116
+
117
+ # Full address of the customer
118
+ def customer_address
119
+ { :address1 => params['address1'], :address2 => params['address2'],
120
+ :city => params['city'], :state => params['state'],
121
+ :country => params['country'], :zipcode => params['zipcode'] }
122
+ end
123
+
124
+ def user_defined
125
+ return @user_defined if @user_defined
126
+ @user_defined = []
127
+ 10.times{ |i| @user_defined.push( params[ "udf#{i+1}" ] ) }
128
+ @user_defined
129
+ end
130
+
131
+ def checksum
132
+ params['hash']
133
+ end
134
+
135
+ def message
136
+ @message || params['error']
137
+ end
138
+
139
+ def checksum_ok?
140
+ fields = user_defined.dup.push( customer_email, customer_first_name, product_info, gross, invoice, :reverse => true )
141
+ fields.unshift( transaction_status )
142
+ unless PayuIn.checksum( *fields ) == checksum
143
+ @message = 'Return checksum not matching the data provided'
144
+ return false
145
+ end
146
+ return true
147
+ end
148
+
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+
@@ -0,0 +1,56 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module PayuIn
5
+ class Return < ActiveMerchant::Billing::Integrations::Return
6
+
7
+ def initialize(query_string, options = {})
8
+ @notification = Notification.new(query_string, options)
9
+ end
10
+
11
+ # PayU Transaction Id
12
+ #
13
+ def transaction_id
14
+ @notification.transaction_id
15
+ end
16
+
17
+ # Returns the status of the transaction as a string
18
+ # The status can be one of the following
19
+ #
20
+ # invalid - transaction id not present
21
+ # tampered - checksum does not mismatch
22
+ # mismatch - order id mismatch
23
+ # success - transaction success
24
+ # pending - transaction pending
25
+ # failure - transaction failure
26
+ #
27
+ # payu does not put the discount field in the checksum
28
+ # it can be easily forged by the attacker without detection
29
+ #
30
+ def status( order_id, order_amount )
31
+ if @notification.invoice_ok?( order_id ) && @notification.amount_ok?( order_amount )
32
+ @notification.status
33
+ else
34
+ 'mismatch'.freeze
35
+ end
36
+ end
37
+
38
+ # check success of the transaction
39
+ # check order_id and
40
+ def success?( order_id, order_amount )
41
+ status( order_id, order_amount ) == 'success'
42
+ end
43
+
44
+ def cancelled?
45
+ false
46
+ end
47
+
48
+ def message
49
+ @notification.message
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,48 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ module Integrations
4
+ module PayuIn
5
+ def self.version
6
+ VERSION
7
+ end
8
+
9
+ module VERSION
10
+ extend Comparable
11
+
12
+ MAJOR = 0
13
+ MINOR = 0
14
+ TINY = 1
15
+ SIGNATURE = [MAJOR, MINOR, TINY]
16
+ STRING = SIGNATURE.join '.'
17
+
18
+ def self.major; MAJOR end
19
+ def self.minor; MINOR end
20
+ def self.tiny; TINY end
21
+ def self.to_s; STRING end
22
+
23
+ def self.hash
24
+ STRING.hash
25
+ end
26
+
27
+ def self.<=>(other)
28
+ other = other.split('.').map { |i| i.to_i } if other.respond_to? :split
29
+ SIGNATURE <=> Array(other)
30
+ end
31
+
32
+ def self.inspect
33
+ STRING.inspect
34
+ end
35
+
36
+ def self.respond_to?(meth, *)
37
+ meth.to_s !~ /^__|^to_str$/ and STRING.respond_to? meth unless super
38
+ end
39
+
40
+ def self.method_missing(meth, *args, &block)
41
+ return super unless STRING.respond_to?(meth)
42
+ STRING.send(meth, *args, &block)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,89 @@
1
+ require 'net/http'
2
+ require 'php_serialize'
3
+
4
+ module ActiveMerchant #:nodoc:
5
+ module Billing #:nodoc:
6
+ module Integrations #:nodoc:
7
+ module PayuIn
8
+
9
+ # PayU.in provides web services to help you with reconciliation of your transactions in real time.
10
+ class WebService < Hash
11
+
12
+ # Add helper method to make api calls using ssl_post, ssl_get
13
+ include PostsData
14
+
15
+ # Whether the invoice is success or not
16
+ # Invoice id is required when multiple invoices are queried at once
17
+ def complete?( invoice_id = nil )
18
+ status( invoice_id ).to_s.downcase == 'success'
19
+ end
20
+
21
+ # Whether the invoice is pending confirmation
22
+ # Invoice id is required when multiple invoices are queried at once
23
+ def pending?( invoice_id = nil )
24
+ status( invoice_id ).to_s.downcase == 'pending'
25
+ end
26
+
27
+ # Returns the payment status of the invoice
28
+ # invoice_id is required if multiple invoices are queried at once
29
+ def status( invoice_id = nil )
30
+ ( invoice_id ? self[ invoice_id.to_s ] : self )[ 'status']
31
+ end
32
+
33
+ # Returns whether the order amount and order discount matches
34
+ # generally in third party shopping carts order discount is handle by the application
35
+ # and should generally by 0.0
36
+ def amount_ok?( order_amount, order_discount = BigDecimal.new( '0.0' ), invoice_id = nil )
37
+ hash = ( invoice_id ? self[ invoice_id.to_s ] : self )
38
+ ( BigDecimal.new( hash[ 'amt' ] ) == order_amount ) && ( BigDecimal.new( hash[ 'disc' ] ) == order_discount )
39
+ end
40
+
41
+ # Returns whether the nett amount after reducing the discounts
42
+ def nett_amount( invoice_id = nil )
43
+ hash = ( invoice_id ? self[ invoice_id.to_s ] : self )
44
+ BigDecimal.new( hash[ 'amt' ] ) - BigDecimal.new( hash[ 'disc' ] )
45
+ end
46
+
47
+ # Returns the PayU.in transaction if for the invocie
48
+ # invoice_id is required if multiple invoices are queried at once
49
+ def transaction_id( invoice_id = nil )
50
+ ( invoice_id ? self[ invoice_id.to_s ] : self )[ 'mihpayid']
51
+ end
52
+
53
+ # Makes verify payment API call to PayU.in web service
54
+ # Params - any number of invoice ids ( provided by the merchant )
55
+ def self.verify_payment( *invoice_ids )
56
+ invoice_string = invoice_ids.join( '|' )
57
+ checksum = PayuIn.checksum( 'verify_payment', invoice_string )
58
+ new.web_service_call( :command => 'verify_payment', :var1 => invoice_string, :hash => checksum, :key => PayuIn.merchant_id )
59
+ end
60
+
61
+ # Makes check payment API call to PayU.in web service
62
+ # Params - PayU.in transaction id
63
+ def self.check_payment( payu_id )
64
+ checksum = PayuIn.checksum( 'check_payment', payu_id )
65
+ new.web_service_call( :command => 'check_payment', :var1 => payu_id, :hash => checksum, :key => PayuIn.merchant_id )
66
+ end
67
+
68
+ # Utility method which makes the web service call and
69
+ # parse the response into WebService object
70
+ def web_service_call( params = {} )
71
+ payload_hash = ActiveMerchant::PostData.new
72
+ payload_hash.merge!( params )
73
+ payload = payload_hash.to_post_data
74
+ response = PHP.unserialize(
75
+ ssl_post(
76
+ PayuIn.web_service_url, payload,
77
+ 'Content-Length' => "#{payload.size}",
78
+ 'User-Agent' => "Active Merchant -- http://activemerchant.org",
79
+ 'service_provider' => "payu_paisa"
80
+ )
81
+ )
82
+ merge!( response["transaction_details"] && response["transaction_details"].size == 1 ? response["transaction_details"].values.first : ( response["transaction_details"] || { :status => response['msg'] } ) )
83
+ end
84
+
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,108 @@
1
+ require 'active_merchant'
2
+ require 'active_merchant/billing/integrations'
3
+ require 'digest/sha2'
4
+ require 'bigdecimal'
5
+
6
+ module ActiveMerchant #:nodoc:
7
+ module Billing #:nodoc:
8
+ module Integrations #:nodoc:
9
+ module PayuIn
10
+ autoload :Return, 'active_merchant/payu_in/return.rb'
11
+ autoload :Helper, 'active_merchant/payu_in/helper.rb'
12
+ autoload :Notification, 'active_merchant/payu_in/notification.rb'
13
+ autoload :WebService, 'active_merchant/payu_in/web_service.rb'
14
+
15
+ mattr_accessor :merchant_id
16
+ mattr_accessor :secret_key
17
+
18
+ # Overwrite this if you want to change the PayU.in test url
19
+ mattr_accessor :test_url
20
+ self.test_url = 'https://test.payu.in/_payment.php'
21
+
22
+ # Overwrite this if you want to change the PayU.in test web
23
+ # service url
24
+ mattr_accessor :test_web_service_url
25
+ self.test_web_service_url = 'https://test.payu.in/merchant/postservice.php'
26
+
27
+ # Overwrite this if you want to change the PayU.in production url
28
+ mattr_accessor :production_url
29
+ self.production_url = 'https://secure.payu.in/_payment.php'
30
+
31
+ # Overwrite this if you want to change the PayU.in production web
32
+ # service url
33
+ mattr_accessor :production_web_service_url
34
+ self.production_web_service_url = 'https://info.payu.in/merchant/postservice.php'
35
+
36
+ def self.new( options = {} )
37
+ self.merchant_id = options[:merchant_id]
38
+ self.secret_key = options[:secret_key]
39
+ self
40
+ end
41
+
42
+ def self.service_url
43
+ mode = ActiveMerchant::Billing::Base.integration_mode
44
+ case mode
45
+ when :production
46
+ self.production_url
47
+ when :test
48
+ self.test_url
49
+ else
50
+ raise StandardError, "Integration mode set to an invalid value: #{mode}"
51
+ end
52
+ end
53
+
54
+ def self.web_service_url
55
+ mode = ActiveMerchant::Billing::Base.integration_mode
56
+ case mode
57
+ when :production
58
+ self.production_web_service_url
59
+ when :test
60
+ self.test_web_service_url
61
+ else
62
+ raise StandardError, "Integration mode set to an invalid value: #{mode}"
63
+ end
64
+ end
65
+
66
+ # Query the PayU server using there web services
67
+ def self.query( command, *args )
68
+ WebService.send( command, *args )
69
+ end
70
+
71
+ def self.return(query_string, options = {})
72
+ Return.new(query_string, options)
73
+ end
74
+
75
+ def self.checksum( *payload_items )
76
+ options = payload_items.pop if Hash === payload_items.last
77
+ options ||= {}
78
+ payload = if options[:reverse] then
79
+ payload_items.dup.push( self.merchant_id || "" ).unshift( self.secret_key || "" ).collect{ |x| trim(x) }.join("|")
80
+ else
81
+ payload_items.dup.unshift( self.merchant_id || "" ).push( self.secret_key || "" ).collect{ |x| trim(x) }.join("|")
82
+ end
83
+ if options[:debug]
84
+ puts "-"*80
85
+ puts payload
86
+ puts "-"*80
87
+ end
88
+ Digest::SHA512.hexdigest( payload )
89
+ end
90
+
91
+ # PayU uses php trim when building the string to compute the checksum
92
+ # trim removes whitespaces from start and end of the string
93
+ # and floats cannot have trailing zeros
94
+ def self.trim(obj)
95
+ # if (BigDecimal === obj)
96
+ # obj.to_s('F')
97
+ # else
98
+ # obj.to_s
99
+ # end.strip.gsub(/(\\d+)\\.0$/){ $1 }.gsub(/(\\d+)(\\.[1-9]+)0+$/){ $1+$2 }
100
+ obj.to_s
101
+ end
102
+
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ require 'active_merchant/payu_in/version'
@@ -0,0 +1,313 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (c) 2003-2009 Thomas Hurst <tom@hur.st>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+
22
+ # PHP serialize() and unserialize() workalikes
23
+ #
24
+ # Release History:
25
+ # 1.0.0 - 2003-06-02 - First release.
26
+ # 1.0.1 - 2003-06-16 - Minor bugfixes.
27
+ # 1.0.2 - 2004-09-17 - Switch all {}'s to explicit Hash.new's.
28
+ # 1.1.0 - 2009-04-01 - Pass assoc to recursive calls (thanks to Edward Speyer).
29
+ # - Serialize Symbol like String.
30
+ # - Add testsuite.
31
+ # - Instantiate auto-generated Structs properly (thanks
32
+ # to Philip Hallstrom).
33
+ # - Unserialize arrays properly in assoc mode.
34
+ # - Add PHP session support (thanks to TJ Vanderpoel).
35
+ # - Release as tarball and gem.
36
+ #
37
+ # See http://www.php.net/serialize and http://www.php.net/unserialize for
38
+ # details on the PHP side of all this.
39
+ module PHP
40
+ # string = PHP.serialize(mixed var[, bool assoc])
41
+ #
42
+ # Returns a string representing the argument in a form PHP.unserialize
43
+ # and PHP's unserialize() should both be able to load.
44
+ #
45
+ # Array, Hash, Fixnum, Float, True/FalseClass, NilClass, String and Struct
46
+ # are supported; as are objects which support the to_assoc method, which
47
+ # returns an array of the form [['attr_name', 'value']..]. Anything else
48
+ # will raise a TypeError.
49
+ #
50
+ # If 'assoc' is specified, Array's who's first element is a two value
51
+ # array will be assumed to be an associative array, and will be serialized
52
+ # as a PHP associative array rather than a multidimensional array.
53
+ def PHP.serialize(var, assoc = false) # {{{
54
+ s = ''
55
+ case var
56
+ when Array
57
+ s << "a:#{var.size}:{"
58
+ if assoc and var.first.is_a?(Array) and var.first.size == 2
59
+ var.each { |k,v|
60
+ s << PHP.serialize(k, assoc) << PHP.serialize(v, assoc)
61
+ }
62
+ else
63
+ var.each_with_index { |v,i|
64
+ s << "i:#{i};#{PHP.serialize(v, assoc)}"
65
+ }
66
+ end
67
+
68
+ s << '}'
69
+
70
+ when Hash
71
+ s << "a:#{var.size}:{"
72
+ var.each do |k,v|
73
+ s << "#{PHP.serialize(k, assoc)}#{PHP.serialize(v, assoc)}"
74
+ end
75
+ s << '}'
76
+
77
+ when Struct
78
+ # encode as Object with same name
79
+ s << "O:#{var.class.to_s.length}:\"#{var.class.to_s.downcase}\":#{var.members.length}:{"
80
+ var.members.each do |member|
81
+ s << "#{PHP.serialize(member, assoc)}#{PHP.serialize(var[member], assoc)}"
82
+ end
83
+ s << '}'
84
+
85
+ when String, Symbol
86
+ s << "s:#{var.to_s.bytesize}:\"#{var.to_s}\";"
87
+
88
+ when Fixnum # PHP doesn't have bignums
89
+ s << "i:#{var};"
90
+
91
+ when Float
92
+ s << "d:#{var};"
93
+
94
+ when NilClass
95
+ s << 'N;'
96
+
97
+ when FalseClass, TrueClass
98
+ s << "b:#{var ? 1 :0};"
99
+
100
+ else
101
+ if var.respond_to?(:to_assoc)
102
+ v = var.to_assoc
103
+ # encode as Object with same name
104
+ s << "O:#{var.class.to_s.length}:\"#{var.class.to_s.downcase}\":#{v.length}:{"
105
+ v.each do |k,v|
106
+ s << "#{PHP.serialize(k.to_s, assoc)}#{PHP.serialize(v, assoc)}"
107
+ end
108
+ s << '}'
109
+ else
110
+ raise TypeError, "Unable to serialize type #{var.class}"
111
+ end
112
+ end
113
+
114
+ s
115
+ end # }}}
116
+
117
+ # string = PHP.serialize_session(mixed var[, bool assoc])
118
+ #
119
+ # Like PHP.serialize, but only accepts a Hash or associative Array as the root
120
+ # type. The results are returned in PHP session format.
121
+ def PHP.serialize_session(var, assoc = false) # {{{
122
+ s = ''
123
+ case var
124
+ when Hash
125
+ var.each do |key,value|
126
+ if key.to_s =~ /\|/
127
+ raise IndexError, "Top level names may not contain pipes"
128
+ end
129
+ s << "#{key}|#{PHP.serialize(value, assoc)}"
130
+ end
131
+ when Array
132
+ var.each do |x|
133
+ case x
134
+ when Array
135
+ if x.size == 2
136
+ s << "#{x[0]}|#{PHP.serialize(x[1])}"
137
+ else
138
+ raise TypeError, "Array is not associative"
139
+ end
140
+ end
141
+ end
142
+ else
143
+ raise TypeError, "Unable to serialize sessions with top level types other than Hash and associative Array"
144
+ end
145
+ s
146
+ end # }}}
147
+
148
+ # mixed = PHP.unserialize(string serialized, [hash classmap, [bool assoc]])
149
+ #
150
+ # Returns an object containing the reconstituted data from serialized.
151
+ #
152
+ # If a PHP array (associative; like an ordered hash) is encountered, it
153
+ # scans the keys; if they're all incrementing integers counting from 0,
154
+ # it's unserialized as an Array, otherwise it's unserialized as a Hash.
155
+ # Note: this will lose ordering. To avoid this, specify assoc=true,
156
+ # and it will be unserialized as an associative array: [[key,value],...]
157
+ #
158
+ # If a serialized object is encountered, the hash 'classmap' is searched for
159
+ # the class name (as a symbol). Since PHP classnames are not case-preserving,
160
+ # this *must* be a .capitalize()d representation. The value is expected
161
+ # to be the class itself; i.e. something you could call .new on.
162
+ #
163
+ # If it's not found in 'classmap', the current constant namespace is searched,
164
+ # and failing that, a new Struct(classname) is generated, with the arguments
165
+ # for .new specified in the same order PHP provided; since PHP uses hashes
166
+ # to represent attributes, this should be the same order they're specified
167
+ # in PHP, but this is untested.
168
+ #
169
+ # each serialized attribute is sent to the new object using the respective
170
+ # {attribute}=() method; you'll get a NameError if the method doesn't exist.
171
+ #
172
+ # Array, Hash, Fixnum, Float, True/FalseClass, NilClass and String should
173
+ # be returned identically (i.e. foo == PHP.unserialize(PHP.serialize(foo))
174
+ # for these types); Struct should be too, provided it's in the namespace
175
+ # Module.const_get within unserialize() can see, or you gave it the same
176
+ # name in the Struct.new(<structname>), otherwise you should provide it in
177
+ # classmap.
178
+ #
179
+ # Note: StringIO is required for unserialize(); it's loaded as needed
180
+ def PHP.unserialize(string, classmap = nil, assoc = false) # {{{
181
+ if classmap == true or classmap == false
182
+ assoc = classmap
183
+ classmap = {}
184
+ end
185
+ classmap ||= {}
186
+
187
+ require 'stringio'
188
+ string = StringIO.new(string)
189
+ def string.read_until(char)
190
+ val = ''
191
+ while (c = self.read(1)) != char
192
+ val << c
193
+ end
194
+ val
195
+ end
196
+
197
+ if string.string =~ /^(\w+)\|/ # session_name|serialized_data
198
+ ret = Hash.new
199
+ loop do
200
+ if string.string[string.pos, 32] =~ /^(\w+)\|/
201
+ string.pos += $&.size
202
+ ret[$1] = PHP.do_unserialize(string, classmap, assoc)
203
+ else
204
+ break
205
+ end
206
+ end
207
+ ret
208
+ else
209
+ PHP.do_unserialize(string, classmap, assoc)
210
+ end
211
+ end
212
+
213
+ private
214
+ def PHP.do_unserialize(string, classmap, assoc)
215
+ val = nil
216
+ # determine a type
217
+ type = string.read(2)[0,1]
218
+ case type
219
+ when 'a' # associative array, a:length:{[index][value]...}
220
+ count = string.read_until('{').to_i
221
+ val = vals = Array.new
222
+ count.times do |i|
223
+ vals << [do_unserialize(string, classmap, assoc), do_unserialize(string, classmap, assoc)]
224
+ end
225
+ string.read(1) # skip the ending }
226
+
227
+ # now, we have an associative array, let's clean it up a bit...
228
+ # arrays have all numeric indexes, in order; otherwise we assume a hash
229
+ array = true
230
+ i = 0
231
+ vals.each do |key,value|
232
+ if key != i # wrong index -> assume hash
233
+ array = false
234
+ break
235
+ end
236
+ i += 1
237
+ end
238
+
239
+ if array
240
+ vals.collect! do |key,value|
241
+ value
242
+ end
243
+ else
244
+ if assoc
245
+ val = vals.map {|v| v }
246
+ else
247
+ val = Hash.new
248
+ vals.each do |key,value|
249
+ val[key] = value
250
+ end
251
+ end
252
+ end
253
+
254
+ when 'O' # object, O:length:"class":length:{[attribute][value]...}
255
+ # class name (lowercase in PHP, grr)
256
+ len = string.read_until(':').to_i + 3 # quotes, seperator
257
+ klass = string.read(len)[1...-2].capitalize.intern # read it, kill useless quotes
258
+
259
+ # read the attributes
260
+ attrs = []
261
+ len = string.read_until('{').to_i
262
+
263
+ len.times do
264
+ attr = (do_unserialize(string, classmap, assoc))
265
+ attrs << [attr.intern, (attr << '=').intern, do_unserialize(string, classmap, assoc)]
266
+ end
267
+ string.read(1)
268
+
269
+ val = nil
270
+ # See if we need to map to a particular object
271
+ if classmap.has_key?(klass)
272
+ val = classmap[klass].new
273
+ elsif Struct.const_defined?(klass) # Nope; see if there's a Struct
274
+ classmap[klass] = val = Struct.const_get(klass)
275
+ val = val.new
276
+ else # Nope; see if there's a Constant
277
+ begin
278
+ classmap[klass] = val = Module.const_get(klass)
279
+
280
+ val = val.new
281
+ rescue NameError # Nope; make a new Struct
282
+ classmap[klass] = Struct.new(klass.to_s, *attrs.collect { |v| v[0].to_s })
283
+ val = val.new
284
+ end
285
+ end
286
+
287
+ attrs.each do |attr,attrassign,v|
288
+ val.__send__(attrassign, v)
289
+ end
290
+
291
+ when 's' # string, s:length:"data";
292
+ len = string.read_until(':').to_i + 3 # quotes, separator
293
+ val = string.read(len)[1...-2] # read it, kill useless quotes
294
+
295
+ when 'i' # integer, i:123
296
+ val = string.read_until(';').to_i
297
+
298
+ when 'd' # double (float), d:1.23
299
+ val = string.read_until(';').to_f
300
+
301
+ when 'N' # NULL, N;
302
+ val = nil
303
+
304
+ when 'b' # bool, b:0 or 1
305
+ val = (string.read(2)[0] == ?1 ? true : false)
306
+
307
+ else
308
+ raise TypeError, "Unable to unserialize type '#{type}'"
309
+ end
310
+
311
+ val
312
+ end # }}}
313
+ end
@@ -0,0 +1,5 @@
1
+ require 'active_merchant_payu_in'
2
+
3
+ describe ActiveMerchantPayuIn do
4
+ it "should behave"
5
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_merchant_payumoney_com
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ram Singla
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-01-31 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.7.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.7.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: activemerchant
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.8'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.8'
46
+ description: PayUMoney.com Payment Gateway integration for ActiveMerchant
47
+ email:
48
+ - ram.singla@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - License
56
+ - Rakefile
57
+ - active_merchant_payu_in.gemspec
58
+ - lib/active_merchant/payu_in/helper.rb
59
+ - lib/active_merchant/payu_in/notification.rb
60
+ - lib/active_merchant/payu_in/return.rb
61
+ - lib/active_merchant/payu_in/version.rb
62
+ - lib/active_merchant/payu_in/web_service.rb
63
+ - lib/active_merchant_payu_in.rb
64
+ - lib/php_serialize.rb
65
+ - pkg/active_merchant_payu_in-0.0.1.gem
66
+ - spec/active_merchant_payu_in_spec.rb
67
+ homepage: http://www.payumoney.com
68
+ licenses: []
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project: active_merchant_payumoney_com
87
+ rubygems_version: 1.8.23
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: PayUMoney.com Payment Gateweay Integration Kit
91
+ test_files: []
92
+ has_rdoc: