alipay_global 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ require 'open-uri'
2
+
3
+ module AlipayGlobal
4
+ module Service
5
+ module Exchange
6
+
7
+ def self.current_rates
8
+ exchange_rates_resp = {}
9
+ open(build_request) do |file|
10
+ file.each_line do |line|
11
+ line = line.strip
12
+ rate_results = line.split("|")
13
+
14
+ exchange_rates_resp[rate_results[2]] = {
15
+ time: DateTime.strptime("#{rate_results[0]} #{rate_results[1]}","%Y%m%d %H%M%S"),
16
+ rate: rate_results[3].to_f
17
+ }
18
+ end
19
+ end
20
+ exchange_rates_resp
21
+ end
22
+
23
+ def self.build_request
24
+ params = {
25
+ 'service' => 'forex_rate_file',
26
+ 'partner' => AlipayGlobal.api_partner_id,
27
+ }
28
+
29
+ AlipayGlobal::Service.request_uri(params).to_s
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ require 'open-uri'
2
+
3
+ module AlipayGlobal
4
+ module Service
5
+ module Notification
6
+
7
+ def self.check(params)
8
+ params = {
9
+ 'service' => 'notify_verify',
10
+ 'partner' => AlipayGlobal.api_partner_id,
11
+ }.merge(params)
12
+
13
+ Net::HTTP.get(AlipayGlobal::Service.request_uri(params,false))
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,73 @@
1
+ require 'open-uri'
2
+
3
+ module AlipayGlobal
4
+ module Service
5
+ module Reconciliation
6
+
7
+ RECONCILIATION_REQUIRED_PARAMS = %w( start_date end_date )
8
+
9
+ RECONCILIATION_ERROR_MESSAGES = [
10
+ "Over 10 days to Date period",
11
+ "Over limit balance amount record",
12
+ "System exception",
13
+ "<alipay><is_success>F</is_success><error>ILLEGAL_PARTNER</error></alipay>",
14
+ "Finish date not ahead of today",
15
+ "Finish date ahead of begin date",
16
+ "Illegal Date period!",
17
+ "Date format incorrect YYYYMMDD",
18
+ "Internet connected exception ,please try later"
19
+ ]
20
+
21
+ def self.request(params)
22
+ reconciliation_resp = []
23
+ open(build_request(params)) do |data|
24
+ results = data.read
25
+
26
+ return false if results.include? "No balance account data in the period"
27
+ raise ArgumentError, "#{results}" if error_check(results)
28
+
29
+ results.each_line do |line|
30
+ line = line.strip
31
+ transaction = line.split("|")
32
+
33
+ reconciliation_resp << {
34
+ partner_transaction_id: transaction[0],
35
+ amount: transaction[1],
36
+ currency: transaction[2],
37
+ transaction_time: valid_alipay_date(transaction[3]) ? DateTime.strptime(transaction[3],"%Y%m%d%H%M%S") : '',
38
+ settlement_time: valid_alipay_date(transaction[4]) ? DateTime.strptime(transaction[4],"%Y%m%d%H%M%S") : '',
39
+ transaction_type: transaction[5],
40
+ service_charge: transaction[6],
41
+ status: transaction[7],
42
+ remarks: transaction[8] ? transaction[8] : ''
43
+ }
44
+ end
45
+
46
+ end
47
+ reconciliation_resp
48
+ end
49
+
50
+ def self.build_request(params)
51
+ AlipayGlobal::Service.check_required_params(params, RECONCILIATION_REQUIRED_PARAMS)
52
+
53
+ params = AlipayGlobal::Utils.stringify_keys(params)
54
+
55
+ params = {
56
+ 'service' => 'forex_compare_file',
57
+ 'partner' => AlipayGlobal.api_partner_id,
58
+ }.merge(params)
59
+
60
+ AlipayGlobal::Service.request_uri(params).to_s
61
+ end
62
+
63
+ def self.error_check(data)
64
+ RECONCILIATION_ERROR_MESSAGES.any? { |w| data =~ /#{w}/ }
65
+ end
66
+
67
+ def self.valid_alipay_date(raw_date)
68
+ raw_date && raw_date.length > 0
69
+ end
70
+
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,67 @@
1
+ require "net/http"
2
+ require "uri"
3
+ require "nokogiri"
4
+
5
+ module AlipayGlobal
6
+ module Service
7
+ module Trade
8
+
9
+ CREATE_TRADE_REQUIRED_PARAMS = %w( notify_url subject out_trade_no currency )
10
+ CREATE_TRADE_OPTIONAL_PARAMS = %w( return_url body total_fee rmb_fee order_gmt_create order_valid_time timeout_rule auth_token supplier seller_id seller_name seller_industry )
11
+
12
+ def self.create(params)
13
+ is_mobile = params.delete(:mobile) || params.delete("mobile")
14
+ service_type = is_mobile ? "create_forex_trade_wap" : "create_forex_trade"
15
+
16
+ params = AlipayGlobal::Utils.stringify_keys(params)
17
+ AlipayGlobal::Service.check_required_params(params, CREATE_TRADE_REQUIRED_PARAMS)
18
+ AlipayGlobal::Service.check_optional_params(params, CREATE_TRADE_OPTIONAL_PARAMS)
19
+
20
+ params = {
21
+ 'service' => service_type,
22
+ '_input_charset' => 'utf-8',
23
+ 'partner' => AlipayGlobal.api_partner_id
24
+ }.merge(params)
25
+
26
+ AlipayGlobal::Service.request_uri(params).to_s
27
+ end
28
+
29
+ def self.refund(refunds)
30
+ file = build_refund_file(refunds)
31
+
32
+ params = {
33
+ 'service' => 'forex_refund_file',
34
+ 'partner' => AlipayGlobal.api_partner_id
35
+ }
36
+
37
+ uri = AlipayGlobal::Service.request_uri(params)
38
+
39
+ #p uri.to_s
40
+
41
+ form_params = { 'partner' => AlipayGlobal.api_partner_id, 'service' => 'forex_refund_file', 'refund_file' => IO.read(file.path) }
42
+
43
+ #p form_params
44
+
45
+ resp = Net::HTTP.post_form(uri, form_params)
46
+
47
+ #p resp.body
48
+
49
+ alipay_resp = Nokogiri::XML(resp.body)
50
+
51
+ alipay_results = alipay_resp.at_xpath('//alipay')
52
+ #puts "success = " + alipay_results.at_xpath('//is_success').content
53
+ #puts "error = " + alipay_results.at_xpath('//error').content
54
+
55
+ file.unlink
56
+ end
57
+
58
+ def self.build_refund_file(refunds)
59
+ file = Tempfile.new(['refund_file','.txt'])
60
+ refund_content = AlipayGlobal::Utils.write_refund_content(refunds)
61
+ file.write(refund_content)
62
+ file.close
63
+ file
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,47 @@
1
+ module AlipayGlobal
2
+ module Sign
3
+ @alipay_rsa_public_key = File.read("#{File.dirname __dir__}/../keys/alipay_public_key.pem")
4
+
5
+ def self.generate(params)
6
+ params = Utils.stringify_keys(params)
7
+ sign_type = AlipayGlobal.sign_type.upcase
8
+ key = AlipayGlobal.api_secret_key
9
+ string = params_to_string(params)
10
+
11
+ case sign_type
12
+ when 'MD5'
13
+ MD5.sign(string, key)
14
+ when 'RSA'
15
+ RSA.sign(string)
16
+ when 'DSA'
17
+ DSA.sign(string, key)
18
+ else
19
+ raise ArgumentError, "invalid sign_type #{sign_type}, allow value: 'MD5', 'RSA', 'DSA'"
20
+ end
21
+ end
22
+
23
+ def self.verify?(params)
24
+ params = Utils.stringify_keys(params)
25
+
26
+ sign_type = params.delete('sign_type')
27
+ signature = params.delete('sign')
28
+ string = params_to_string(params)
29
+
30
+ case sign_type
31
+ when 'MD5'
32
+ key = AlipayGlobal.api_secret_key
33
+ MD5.verify?(string, key, signature)
34
+ when 'RSA'
35
+ RSA.verify?(string, @alipay_rsa_public_key, signature)
36
+ when 'DSA'
37
+ DSA.verify?(string, signature)
38
+ else
39
+ false
40
+ end
41
+ end
42
+
43
+ def self.params_to_string(params)
44
+ params.sort.map { |item| item.join('=') }.join('&')
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,13 @@
1
+ module AlipayGlobal
2
+ module Sign
3
+ class DSA
4
+ def self.sign(string, private_key)
5
+ raise NotImplementedError, 'DSA is not implemented'
6
+ end
7
+
8
+ def self.verify?(string, sign)
9
+ raise NotImplementedError, 'DSA is not implemented'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ require 'digest/md5'
2
+
3
+ module AlipayGlobal
4
+ module Sign
5
+ class MD5
6
+ #pre-signed string should not be url encoded
7
+ def self.sign(string, secret_key)
8
+ Digest::MD5.hexdigest("#{string}#{secret_key}")
9
+ end
10
+
11
+ def self.verify?(string, secret_key, sign)
12
+ sign == sign(string, secret_key)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module AlipayGlobal
5
+ module Sign
6
+ class RSA
7
+ def self.private_key
8
+ raise ArgumentError, "Assign valid location for RSA private key location :: #AlipayGlobal.private_key_location = #{AlipayGlobal.private_key_location}" if !AlipayGlobal.private_key_location
9
+ File.read(AlipayGlobal.private_key_location)
10
+ end
11
+
12
+ def self.sign(string, supplied_private_key = nil)
13
+ key = supplied_private_key || private_key
14
+ rsa = OpenSSL::PKey::RSA.new(key)
15
+ Base64.encode64(rsa.sign(OpenSSL::Digest::SHA256.new, string))
16
+ end
17
+
18
+ def self.verify?(string, public_key, sign)
19
+ rsa = OpenSSL::PKey::RSA.new(public_key)
20
+ rsa.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(sign), string)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,30 @@
1
+ module AlipayGlobal
2
+ module Utils
3
+ def self.stringify_keys(hash)
4
+ new_hash = {}
5
+ hash.each do |key, value|
6
+ case key
7
+ when :total_fee, :rmb_fee, :refund_sum
8
+ value = value.round(2)
9
+ value = '%.2f' % value
10
+ end
11
+ new_hash[(key.to_s rescue key) || key] = value
12
+ end
13
+ new_hash
14
+ end
15
+
16
+ def self.write_refund_content(refunds)
17
+ raise ArgumentException, "Refund content should contain at least 1 refund object" if refunds.length == 0
18
+ file_content = ""
19
+ refunds.each do |refund|
20
+ refund = stringify_keys(refund)
21
+ refund_reason = refund['refund_reason'] || ""
22
+ line_ending = stringify_keys(refunds.last) == refund ? "" : "\n"
23
+ raise ArgumentException, "Refund reason (#{refund_reason} cannot be more than 255 characters long)" if refund_reason.length > 255
24
+ file_content += "#{AlipayGlobal.api_partner_id}|#{refund['new_transaction_id']}|#{refund['old_transaction_id']}|#{refund['currency']}|#{refund['refund_sum']}|#{refund['refund_time']}|#{refund_reason}#{line_ending}"
25
+ end
26
+ file_content
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module AlipayGlobal
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,31 @@
1
+ require 'test_config'
2
+
3
+ describe "AlipayGlobal", "basic gem config" do
4
+
5
+ before do
6
+ @alipay = AlipayGlobal
7
+ end
8
+
9
+ it "has a basic debug mode default" do
10
+ assert @alipay.debug_mode?
11
+ end
12
+
13
+ it "has a default MD5 mode" do
14
+ assert_equal 'MD5', @alipay.sign_type
15
+ end
16
+
17
+ describe "api_partner_id & api_secret_key assignment" do
18
+
19
+ before do
20
+ @alipay.api_partner_id = '2088101122136241'
21
+ @alipay.api_secret_key = '760bdzec6y9goq7ctyx96ezkz78287de'
22
+ end
23
+
24
+ it "has assigned api_partner_id and api_secret_key" do
25
+ @alipay.api_partner_id.wont_be_nil true
26
+ @alipay.api_secret_key.wont_be_nil true
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,19 @@
1
+ require 'test_config'
2
+
3
+ describe "AlipayGlobal::Service::Exchange", "Forex exchange rates request" do
4
+
5
+ before do
6
+ @alipay = AlipayGlobal
7
+ @alipay.api_partner_id = '2088101122136241'
8
+ @alipay.api_secret_key = '760bdzec6y9goq7ctyx96ezkz78287de'
9
+ end
10
+
11
+ describe "#build_request" do
12
+
13
+ it "should create the correct rates url" do
14
+ assert_equal 'https://mapi.alipay.net/gateway.do?service=forex_rate_file&partner=2088101122136241&sign_type=MD5&sign=4051af1716d522b47acff927d4fb9781', @alipay::Service::Exchange.build_request()
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,21 @@
1
+ require 'test_config'
2
+
3
+ describe "AlipayGlobal::Service::Notification", "Notification request check" do
4
+
5
+ before do
6
+ @alipay = AlipayGlobal
7
+ @alipay.api_partner_id = '2088101122136241'
8
+ @alipay.api_secret_key = '760bdzec6y9goq7ctyx96ezkz78287de'
9
+
10
+ @params = {
11
+ notify_id: 'abcdefghijklmnopqrst'
12
+ }
13
+ end
14
+
15
+ describe "#check" do
16
+ it "should get params correctly" do
17
+ assert_equal "false", @alipay::Service::Notification.check(@params)
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,83 @@
1
+ require 'test_config'
2
+
3
+ describe "AlipayGlobal::Service::Reconciliation", "Reconciliation request check" do
4
+
5
+ before do
6
+ @alipay = AlipayGlobal
7
+ @alipay.api_partner_id = '2088101122136241'
8
+ @alipay.api_secret_key = '760bdzec6y9goq7ctyx96ezkz78287de'
9
+
10
+ @params = {
11
+ 'start_date'=> '20120202',
12
+ 'end_date'=> '20120205'
13
+ }
14
+ end
15
+
16
+ describe "#build_request" do
17
+ it "should get params correctly" do
18
+ assert_equal "https://mapi.alipay.net/gateway.do?service=forex_compare_file&partner=2088101122136241&start_date=20120202&end_date=20120205&sign_type=MD5&sign=3d09abe0980696bb39bb72c6137266ca", @alipay::Service::Reconciliation.build_request(@params)
19
+ end
20
+ end
21
+
22
+ describe "#request" do
23
+ #Alipay has some weird ass success parameter
24
+ describe "Success scenario" do
25
+ #this test is dependent on interfacing with alipay's test environment. The data may change/disappear over time. This test will fail if we get no data for the given parameter
26
+ it "should return the content parsed correctly" do
27
+ assert_operator 0, :<, @alipay::Service::Reconciliation.request({
28
+ 'start_date'=> '20122340',
29
+ 'end_date'=> '20122349'
30
+ }).length
31
+ end
32
+ end
33
+
34
+ describe "Error scenarios" do
35
+
36
+ it "return false for no records in period" do
37
+ assert_equal false, @alipay::Service::Reconciliation.request(@params)
38
+ end
39
+
40
+ it "should throw an ArgumentError for date range > 10 days" do
41
+ proc{ (@alipay::Service::Reconciliation.request({
42
+ 'start_date'=> '20120202',
43
+ 'end_date'=> '20120305'
44
+ })).call }.must_raise(ArgumentError)
45
+ end
46
+
47
+ it "should throw an ArgumentError for an errorneous date range" do
48
+ proc{ (@alipay::Service::Reconciliation.request({
49
+ 'start_date'=> '20130202',
50
+ 'end_date'=> '20120305'
51
+ })).call }.must_raise(ArgumentError)
52
+ end
53
+
54
+ it "should throw an ArgumentError for no dates supplied (or nil)" do
55
+ proc{ (@alipay::Service::Reconciliation.request({})).call }.must_raise(ArgumentError)
56
+ proc{ (@alipay::Service::Reconciliation.request(nil)).call }.must_raise(NoMethodError)
57
+ end
58
+
59
+ it "should throw an ArgumentError for finish date ahead of today" do
60
+ proc{ (@alipay::Service::Reconciliation.request({
61
+ 'start_date'=> '20151300',
62
+ 'end_date'=> '20151333'
63
+ })).call }.must_raise(ArgumentError)
64
+ end
65
+
66
+ describe "Incorrect Merchant" do
67
+ before do
68
+ @alipay.api_partner_id = 'daftpunk'
69
+ end
70
+
71
+ it "should throw an ArgumentError for Merchant ID incorrect" do
72
+ proc{ (@alipay::Service::Reconciliation.request(@params)).call }.must_raise(ArgumentError)
73
+ end
74
+
75
+ after do
76
+ @alipay.api_partner_id = '2088101122136241'
77
+ end
78
+ end
79
+
80
+ end
81
+ end
82
+
83
+ end