From 8a77f24310b5f289bf914d26d22e0dccfdf08395 Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 25 Jun 2017 12:17:32 +0800 Subject: [PATCH 01/56] Init Alipay::Client Class --- lib/alipay.rb | 1 + lib/alipay/client.rb | 13 +++++++++++++ test/alipay/client_test.rb | 19 +++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 lib/alipay/client.rb create mode 100644 test/alipay/client_test.rb diff --git a/lib/alipay.rb b/lib/alipay.rb index ae3e738..e4d0971 100644 --- a/lib/alipay.rb +++ b/lib/alipay.rb @@ -16,6 +16,7 @@ require 'alipay/mobile/sign' require 'alipay/app/service' require 'alipay/app/sign' +require 'alipay/client' module Alipay @debug_mode = true diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb new file mode 100644 index 0000000..23ffa22 --- /dev/null +++ b/lib/alipay/client.rb @@ -0,0 +1,13 @@ +module Alipay + class Client + def initialize(url:, app_id:, app_private_key:, format: 'json', charset: 'utf-8', alipay_public_key:, sign_type: 'RSA2') + @url = url + @app_id = app_id + @app_private_key = app_private_key + @format = format + @charset = charset + @alipay_public_key = alipay_public_key + @sign_type = sign_type + end + end +end diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb new file mode 100644 index 0000000..0e36bdf --- /dev/null +++ b/test/alipay/client_test.rb @@ -0,0 +1,19 @@ +require 'test_helper' + +class Alipay::ClientTest < Minitest::Test + def setup + @client = Alipay::Client.new( + url: 'https://openapi.alipaydev.com/gateway.do', + app_id: '2016000000000000', + app_private_key: TEST_RSA_PRIVATE_KEY, + format: 'json', + charset: 'utf-8', + alipay_public_key: TEST_RSA_PUBLIC_KEY, + sign_type: 'RSA2' + ) + end + + def test_client_initialize + refute_nil @client + end +end From 8b88e439facd44b3f498a6dd42f46f23c48614bb Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 25 Jun 2017 20:39:47 +0800 Subject: [PATCH 02/56] add Alipay::Client#request_url --- .gitignore | 1 + bin/console | 14 +++++++++++++ config.yml.example | 6 ++++++ lib/alipay/client.rb | 42 ++++++++++++++++++++++++++++++-------- test/alipay/client_test.rb | 4 ++++ 5 files changed, 59 insertions(+), 8 deletions(-) create mode 100755 bin/console create mode 100644 config.yml.example diff --git a/.gitignore b/.gitignore index fc398a0..65044df 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ spec/reports test/tmp test/version_tmp tmp +config.yml diff --git a/bin/console b/bin/console new file mode 100755 index 0000000..e927e10 --- /dev/null +++ b/bin/console @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby + +require 'bundler/setup' +require 'alipay' +require 'yaml' +require 'json' + +if File.exists?('config.yml') + $alipay_client = Alipay::Client.new(YAML.load_file('config.yml')) +end + +require 'irb' + +IRB.start(__FILE__) diff --git a/config.yml.example b/config.yml.example new file mode 100644 index 0000000..00a3a81 --- /dev/null +++ b/config.yml.example @@ -0,0 +1,6 @@ +# Dev config for bin/console + +url: +app_id: +app_private_key: +alipay_public_key: diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index 23ffa22..a11704d 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -1,13 +1,39 @@ module Alipay class Client - def initialize(url:, app_id:, app_private_key:, format: 'json', charset: 'utf-8', alipay_public_key:, sign_type: 'RSA2') - @url = url - @app_id = app_id - @app_private_key = app_private_key - @format = format - @charset = charset - @alipay_public_key = alipay_public_key - @sign_type = sign_type + def initialize(options) + options = ::Alipay::Utils.stringify_keys(options) + @url = options['url'] + @app_id = options['app_id'] + @app_private_key = options['app_private_key'] + @format = options['format'] || 'json' + @charset = options['charset'] || 'utf-8' + @alipay_public_key = options['alipay_public_key'] + @sign_type = options['sign_type'] || 'RSA2' + end + + def request_url(options = {}) + options = { + app_id: @app_id, + charset: @charset, + sign_type: @sign_type, + version: '1.0', + timestamp: Time.now.localtime('+08:00').strftime("%Y-%m-%d %H:%M:%S") + }.merge(options) + + string = options.sort.map { |item| item.join('=') }.join('&') + + sign = case @sign_type + when 'RSA' + ::Alipay::Sign::RSA.sign(@app_private_key, string) + when 'RSA2' + ::Alipay::Sign::RSA2.sign(@app_private_key, string) + else + raise "Unsupported sign_type: #{@sign_type}" + end + + uri = URI(@url) + uri.query = URI.encode_www_form(options.merge(sign: sign)) + uri.to_s end end end diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index 0e36bdf..2700d8b 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -16,4 +16,8 @@ def setup def test_client_initialize refute_nil @client end + + def test_request_url_for_alipay_trade_page_pay + assert_equal 'https://openapi.alipaydev.com/gateway.do?app_id=2016000000000000&charset=utf-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22FAST_INSTANT_TRADE_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=Dyx7%2BjfeGF4qgBHjJt6i2mJ9brI6uamBeq%2BH6SLe44CjtphTpBq5HUeuvGI7Dhar%2FzgfMP0tzBCwEjb%2FKdwG3ZdcyB6jOWzwi5YX3fVjLx%2FsFY5qr%2Fng%2BCuEsuZ5QId%2FLGQnp04cT%2BefayR20qfMwmAjQVowBBy3KsKb7iYdn6k%3D', @client.request_url(method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' }.to_json, timestamp: '2016-04-01 00:00:00') + end end From 6f3a0e69f4a0552d42d687489d569f2e852bc692 Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 25 Jun 2017 20:49:48 +0800 Subject: [PATCH 03/56] remove test file and bin file in gemspec --- alipay.gemspec | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/alipay.gemspec b/alipay.gemspec index 063fe2b..6947be4 100644 --- a/alipay.gemspec +++ b/alipay.gemspec @@ -13,9 +13,10 @@ Gem::Specification.new do |spec| spec.homepage = "https://github.com/chloerei/alipay" spec.license = "MIT" - spec.files = `git ls-files`.split($/) - spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } - spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) + spec.files = `git ls-files -z`.split("\x0").reject do |f| + f.match(%r{^(test|spec|features)/}) + end + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] spec.add_development_dependency "bundler", "~> 1.3" From ee4d6d812fc954392a097b6ecc892d0fb71a8ba8 Mon Sep 17 00:00:00 2001 From: Rei Date: Mon, 26 Jun 2017 21:43:00 +0800 Subject: [PATCH 04/56] Add Alipay::Client#execute --- lib/alipay/client.rb | 30 +++++++++++++++++++++--------- test/alipay/client_test.rb | 16 ++++++++++++++++ 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index a11704d..267fb58 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -11,18 +11,34 @@ def initialize(options) @sign_type = options['sign_type'] || 'RSA2' end - def request_url(options = {}) - options = { + def default_params + { app_id: @app_id, charset: @charset, sign_type: @sign_type, version: '1.0', timestamp: Time.now.localtime('+08:00').strftime("%Y-%m-%d %H:%M:%S") - }.merge(options) + } + end + + def request_url(params) + params = default_params.merge(params) + + uri = URI(@url) + uri.query = URI.encode_www_form(params.merge(sign: generate_sign(params))) + uri.to_s + end - string = options.sort.map { |item| item.join('=') }.join('&') + def execute(params) + params = default_params.merge(params) - sign = case @sign_type + Net::HTTP.post_form(URI(@url), params.merge(sign: generate_sign(params))).body + end + + def generate_sign(params) + string = params.sort.map { |item| item.join('=') }.join('&') + + case @sign_type when 'RSA' ::Alipay::Sign::RSA.sign(@app_private_key, string) when 'RSA2' @@ -30,10 +46,6 @@ def request_url(options = {}) else raise "Unsupported sign_type: #{@sign_type}" end - - uri = URI(@url) - uri.query = URI.encode_www_form(options.merge(sign: sign)) - uri.to_s end end end diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index 2700d8b..5dafb20 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -20,4 +20,20 @@ def test_client_initialize def test_request_url_for_alipay_trade_page_pay assert_equal 'https://openapi.alipaydev.com/gateway.do?app_id=2016000000000000&charset=utf-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22FAST_INSTANT_TRADE_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=Dyx7%2BjfeGF4qgBHjJt6i2mJ9brI6uamBeq%2BH6SLe44CjtphTpBq5HUeuvGI7Dhar%2FzgfMP0tzBCwEjb%2FKdwG3ZdcyB6jOWzwi5YX3fVjLx%2FsFY5qr%2Fng%2BCuEsuZ5QId%2FLGQnp04cT%2BefayR20qfMwmAjQVowBBy3KsKb7iYdn6k%3D', @client.request_url(method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' }.to_json, timestamp: '2016-04-01 00:00:00') end + + def test_execute_for_data_dataservice_bill_downloadurl_query + body = <<-EOF + { + "sign":"ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE", + "alipay_data_dataservice_bill_downloadurl_query_response":{ + "code":"10000", + "bill_download_url":"http://dwbillcenter.alipay.com/downloadBillFile.resource?bizType=X&userId=X&fileType=X&bizDates=X&downloadFileName=X&fileId=X", + "msg":"Success" + } + } + EOF + stub_request(:post, "https://openapi.alipaydev.com/gateway.do"). + to_return(body: body) + assert_equal body, @client.execute(method: 'data.dataservice.bill.downloadurl.query', biz_content: { bill_type: 'trade', bill_date: '2016-04-01' }) + end end From 715390406f5e8609e62a7fccd07be74547fdaa39 Mon Sep 17 00:00:00 2001 From: Rei Date: Mon, 26 Jun 2017 22:27:14 +0800 Subject: [PATCH 05/56] Add Alipay::Client#request_form --- lib/alipay/client.rb | 19 +++++++++++++++++-- test/alipay/client_test.rb | 11 ++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index 267fb58..9b9d4ca 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -23,16 +23,31 @@ def default_params def request_url(params) params = default_params.merge(params) + params[:sign] = generate_sign(params) uri = URI(@url) - uri.query = URI.encode_www_form(params.merge(sign: generate_sign(params))) + uri.query = URI.encode_www_form(params) uri.to_s end + def request_form(params) + params = default_params.merge(params) + params[:sign] = generate_sign(params) + + html = %Q(
) + params.each do |key, value| + html << %Q() + end + html << "
" + html << "" + html + end + def execute(params) params = default_params.merge(params) + params[:sign] = generate_sign(params) - Net::HTTP.post_form(URI(@url), params.merge(sign: generate_sign(params))).body + Net::HTTP.post_form(URI(@url), params).body end def generate_sign(params) diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index 5dafb20..f41e481 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -18,7 +18,15 @@ def test_client_initialize end def test_request_url_for_alipay_trade_page_pay - assert_equal 'https://openapi.alipaydev.com/gateway.do?app_id=2016000000000000&charset=utf-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22FAST_INSTANT_TRADE_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=Dyx7%2BjfeGF4qgBHjJt6i2mJ9brI6uamBeq%2BH6SLe44CjtphTpBq5HUeuvGI7Dhar%2FzgfMP0tzBCwEjb%2FKdwG3ZdcyB6jOWzwi5YX3fVjLx%2FsFY5qr%2Fng%2BCuEsuZ5QId%2FLGQnp04cT%2BefayR20qfMwmAjQVowBBy3KsKb7iYdn6k%3D', @client.request_url(method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' }.to_json, timestamp: '2016-04-01 00:00:00') + url = 'https://openapi.alipaydev.com/gateway.do?app_id=2016000000000000&charset=utf-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22FAST_INSTANT_TRADE_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=Dyx7%2BjfeGF4qgBHjJt6i2mJ9brI6uamBeq%2BH6SLe44CjtphTpBq5HUeuvGI7Dhar%2FzgfMP0tzBCwEjb%2FKdwG3ZdcyB6jOWzwi5YX3fVjLx%2FsFY5qr%2Fng%2BCuEsuZ5QId%2FLGQnp04cT%2BefayR20qfMwmAjQVowBBy3KsKb7iYdn6k%3D' + + assert_equal url, @client.request_url(method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' }.to_json, timestamp: '2016-04-01 00:00:00') + end + + def test_request_form_for_alipay_trade_page_pay + form = "
" + + assert_equal form, @client.request_form(method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' }.to_json, timestamp: '2016-04-01 00:00:00') end def test_execute_for_data_dataservice_bill_downloadurl_query @@ -34,6 +42,7 @@ def test_execute_for_data_dataservice_bill_downloadurl_query EOF stub_request(:post, "https://openapi.alipaydev.com/gateway.do"). to_return(body: body) + assert_equal body, @client.execute(method: 'data.dataservice.bill.downloadurl.query', biz_content: { bill_type: 'trade', bill_date: '2016-04-01' }) end end From f326ede23c8bdadd0593513adb78e865ecf14b44 Mon Sep 17 00:00:00 2001 From: Rei Date: Mon, 26 Jun 2017 22:34:54 +0800 Subject: [PATCH 06/56] fix test data --- test/alipay/client_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index f41e481..4313206 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -43,6 +43,6 @@ def test_execute_for_data_dataservice_bill_downloadurl_query stub_request(:post, "https://openapi.alipaydev.com/gateway.do"). to_return(body: body) - assert_equal body, @client.execute(method: 'data.dataservice.bill.downloadurl.query', biz_content: { bill_type: 'trade', bill_date: '2016-04-01' }) + assert_equal body, @client.execute(method: 'alipay.data.dataservice.bill.downloadurl.query', biz_content: { bill_type: 'trade', bill_date: '2016-04-01' }) end end From 72c0abf7c4d75cccd803b987800242a23c17053c Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 2 Jul 2017 17:16:50 +0800 Subject: [PATCH 07/56] extract prepare_params method --- lib/alipay/client.rb | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index 9b9d4ca..147913a 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -11,28 +11,15 @@ def initialize(options) @sign_type = options['sign_type'] || 'RSA2' end - def default_params - { - app_id: @app_id, - charset: @charset, - sign_type: @sign_type, - version: '1.0', - timestamp: Time.now.localtime('+08:00').strftime("%Y-%m-%d %H:%M:%S") - } - end - def request_url(params) - params = default_params.merge(params) - params[:sign] = generate_sign(params) - + params = prepare_params(params) uri = URI(@url) uri.query = URI.encode_www_form(params) uri.to_s end def request_form(params) - params = default_params.merge(params) - params[:sign] = generate_sign(params) + params = prepare_params(params) html = %Q(
) params.each do |key, value| @@ -44,13 +31,12 @@ def request_form(params) end def execute(params) - params = default_params.merge(params) - params[:sign] = generate_sign(params) + params = prepare_params(params) Net::HTTP.post_form(URI(@url), params).body end - def generate_sign(params) + def sign(params) string = params.sort.map { |item| item.join('=') }.join('&') case @sign_type @@ -62,5 +48,19 @@ def generate_sign(params) raise "Unsupported sign_type: #{@sign_type}" end end + + private + + def prepare_params(params) + params = { + 'app_id' => @app_id, + 'charset' => @charset, + 'sign_type' => @sign_type, + 'version' => '1.0', + 'timestamp' => Time.now.localtime('+08:00').strftime("%Y-%m-%d %H:%M:%S") + }.merge(::Alipay::Utils.stringify_keys(params)) + params['sign'] = sign(params) + params + end end end From f9290ed3ddb5ff3c54a2f9a4c818e65e93476af1 Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 2 Jul 2017 17:29:48 +0800 Subject: [PATCH 08/56] rename page_execute method, more like official namming --- lib/alipay/client.rb | 4 ++-- test/alipay/client_test.rb | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index 147913a..7680e6f 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -11,14 +11,14 @@ def initialize(options) @sign_type = options['sign_type'] || 'RSA2' end - def request_url(params) + def page_execute_url(params) params = prepare_params(params) uri = URI(@url) uri.query = URI.encode_www_form(params) uri.to_s end - def request_form(params) + def page_execute_form(params) params = prepare_params(params) html = %Q() diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index 4313206..23e7f25 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -17,16 +17,16 @@ def test_client_initialize refute_nil @client end - def test_request_url_for_alipay_trade_page_pay + def test_page_execute_url_for_alipay_trade_page_pay url = 'https://openapi.alipaydev.com/gateway.do?app_id=2016000000000000&charset=utf-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22FAST_INSTANT_TRADE_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=Dyx7%2BjfeGF4qgBHjJt6i2mJ9brI6uamBeq%2BH6SLe44CjtphTpBq5HUeuvGI7Dhar%2FzgfMP0tzBCwEjb%2FKdwG3ZdcyB6jOWzwi5YX3fVjLx%2FsFY5qr%2Fng%2BCuEsuZ5QId%2FLGQnp04cT%2BefayR20qfMwmAjQVowBBy3KsKb7iYdn6k%3D' - assert_equal url, @client.request_url(method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' }.to_json, timestamp: '2016-04-01 00:00:00') + assert_equal url, @client.page_execute_url(method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' }.to_json, timestamp: '2016-04-01 00:00:00') end - def test_request_form_for_alipay_trade_page_pay + def test_page_execute_form_for_alipay_trade_page_pay form = "" - assert_equal form, @client.request_form(method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' }.to_json, timestamp: '2016-04-01 00:00:00') + assert_equal form, @client.page_execute_form(method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' }.to_json, timestamp: '2016-04-01 00:00:00') end def test_execute_for_data_dataservice_bill_downloadurl_query From 8d34de0cfef8449e9c3d6bcdb7171574dfb7a0c2 Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 2 Jul 2017 17:36:03 +0800 Subject: [PATCH 09/56] add sdk_execute --- lib/alipay/client.rb | 5 +++++ test/alipay/client_test.rb | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index 7680e6f..f2afc30 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -11,6 +11,11 @@ def initialize(options) @sign_type = options['sign_type'] || 'RSA2' end + def sdk_execute(params) + params = prepare_params(params) + URI.encode_www_form(params) + end + def page_execute_url(params) params = prepare_params(params) uri = URI(@url) diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index 23e7f25..2c38d39 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -17,6 +17,12 @@ def test_client_initialize refute_nil @client end + def test_sdk_execute_for_alipay_trade_app_pay + string = 'app_id=2016000000000000&charset=utf-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=Ws9J1PwRZx9Ku65rvRcbmy%2BhpfEpzeKculXbpa%2FuZbWfr4m2keutr63RQPhXqhWMKNGXh9BuzbX9khWZOa12YijxDljSW%2FCmtncsFoUpordhVVg6xGtjJqF28mRbx3uqmjxmThyRoa6taDKYB1qtAdoTfEnkfVG2zvR%2BZ6jg3dE%3D' + + assert_equal string, @client.sdk_execute(method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', product_code: 'QUICK_MSECURITY_PAY', total_amount: '0.01', subject: 'test' }.to_json, timestamp: '2016-04-01 00:00:00') + end + def test_page_execute_url_for_alipay_trade_page_pay url = 'https://openapi.alipaydev.com/gateway.do?app_id=2016000000000000&charset=utf-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22FAST_INSTANT_TRADE_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=Dyx7%2BjfeGF4qgBHjJt6i2mJ9brI6uamBeq%2BH6SLe44CjtphTpBq5HUeuvGI7Dhar%2FzgfMP0tzBCwEjb%2FKdwG3ZdcyB6jOWzwi5YX3fVjLx%2FsFY5qr%2Fng%2BCuEsuZ5QId%2FLGQnp04cT%2BefayR20qfMwmAjQVowBBy3KsKb7iYdn6k%3D' From 7ee4498f39b4049b99d696c2f9efc7cd6a2013a0 Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 2 Jul 2017 17:39:32 +0800 Subject: [PATCH 10/56] format client test --- test/alipay/client_test.rb | 41 ++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index 2c38d39..54875a2 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -20,19 +20,46 @@ def test_client_initialize def test_sdk_execute_for_alipay_trade_app_pay string = 'app_id=2016000000000000&charset=utf-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=Ws9J1PwRZx9Ku65rvRcbmy%2BhpfEpzeKculXbpa%2FuZbWfr4m2keutr63RQPhXqhWMKNGXh9BuzbX9khWZOa12YijxDljSW%2FCmtncsFoUpordhVVg6xGtjJqF28mRbx3uqmjxmThyRoa6taDKYB1qtAdoTfEnkfVG2zvR%2BZ6jg3dE%3D' - assert_equal string, @client.sdk_execute(method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', product_code: 'QUICK_MSECURITY_PAY', total_amount: '0.01', subject: 'test' }.to_json, timestamp: '2016-04-01 00:00:00') + assert_equal string, @client.sdk_execute( + method: 'alipay.trade.page.pay', + biz_content: { + out_trade_no: '20160401000000', + product_code: 'QUICK_MSECURITY_PAY', + total_amount: '0.01', + subject: 'test' + }.to_json, + timestamp: '2016-04-01 00:00:00' + ) end def test_page_execute_url_for_alipay_trade_page_pay url = 'https://openapi.alipaydev.com/gateway.do?app_id=2016000000000000&charset=utf-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22FAST_INSTANT_TRADE_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=Dyx7%2BjfeGF4qgBHjJt6i2mJ9brI6uamBeq%2BH6SLe44CjtphTpBq5HUeuvGI7Dhar%2FzgfMP0tzBCwEjb%2FKdwG3ZdcyB6jOWzwi5YX3fVjLx%2FsFY5qr%2Fng%2BCuEsuZ5QId%2FLGQnp04cT%2BefayR20qfMwmAjQVowBBy3KsKb7iYdn6k%3D' - assert_equal url, @client.page_execute_url(method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' }.to_json, timestamp: '2016-04-01 00:00:00') + assert_equal url, @client.page_execute_url( + method: 'alipay.trade.page.pay', + biz_content: { + out_trade_no: '20160401000000', + product_code: 'FAST_INSTANT_TRADE_PAY', + total_amount: '0.01', + subject: 'test' + }.to_json, + timestamp: '2016-04-01 00:00:00' + ) end def test_page_execute_form_for_alipay_trade_page_pay form = "
" - assert_equal form, @client.page_execute_form(method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' }.to_json, timestamp: '2016-04-01 00:00:00') + assert_equal form, @client.page_execute_form( + method: 'alipay.trade.page.pay', + biz_content: { + out_trade_no: '20160401000000', + product_code: 'FAST_INSTANT_TRADE_PAY', + total_amount: '0.01', + subject: 'test' + }.to_json, + timestamp: '2016-04-01 00:00:00' + ) end def test_execute_for_data_dataservice_bill_downloadurl_query @@ -49,6 +76,12 @@ def test_execute_for_data_dataservice_bill_downloadurl_query stub_request(:post, "https://openapi.alipaydev.com/gateway.do"). to_return(body: body) - assert_equal body, @client.execute(method: 'alipay.data.dataservice.bill.downloadurl.query', biz_content: { bill_type: 'trade', bill_date: '2016-04-01' }) + assert_equal body, @client.execute( + method: 'alipay.data.dataservice.bill.downloadurl.query', + biz_content: { + bill_type: 'trade', + bill_date: '2016-04-01' + }.to_json + ) end end From 47b5d3f21db63b4ad8eae481d5e1aa5d9432bb80 Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 2 Jul 2017 18:06:42 +0800 Subject: [PATCH 11/56] add verify method --- lib/alipay/client.rb | 17 +++++++++++++++++ test/alipay/client_test.rb | 10 ++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index f2afc30..ee3e7af 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -54,6 +54,23 @@ def sign(params) end end + def verify?(params) + params = Utils.stringify_keys(params) + return false if params['sign_type'] != @sign_type + + sign = params.delete('sign') + string = params.sort.map { |item| item.join('=') }.join('&') + case @sign_type + when 'RSA' + ::Alipay::Sign::RSA.verify?(@app_private_key, string, sign) + when 'RSA2' + ::Alipay::Sign::RSA2.verify?(@app_private_key, string, sign) + else + raise "Unsupported sign_type: #{@sign_type}" + end + Utils.secure_compare(sign(params), sign) + end + private def prepare_params(params) diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index 54875a2..a0c9a7f 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -84,4 +84,14 @@ def test_execute_for_data_dataservice_bill_downloadurl_query }.to_json ) end + + # Use pair rsa key so we can test it + def test_verify + params = { + out_trade_no: '20160401000000', + trade_status: 'TRADE_SUCCESS' + } + params[:sign] = @client.sign(params) + @client.verify?(params) + end end From 7334a1d35e625a4754a1cb4026d2f81a0c3373c0 Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 2 Jul 2017 19:06:32 +0800 Subject: [PATCH 12/56] update Readme, create legacy api document. --- .gitignore | 1 - LICENSE.txt | 2 +- README.md | 794 ++------------------------------------------- doc/legacy_api.md | 812 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 837 insertions(+), 772 deletions(-) create mode 100644 doc/legacy_api.md diff --git a/.gitignore b/.gitignore index 65044df..e6428fa 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ Gemfile.lock InstalledFiles _yardoc coverage -doc/ lib/bundler/man pkg rdoc diff --git a/LICENSE.txt b/LICENSE.txt index 5fc17a6..943b502 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2013 Rei +Copyright (c) 2017 Rei MIT License diff --git a/README.md b/README.md index 33cd74a..9c925a9 100644 --- a/README.md +++ b/README.md @@ -1,793 +1,49 @@ # Alipay -A unofficial alipay ruby gem. +Unofficial alipay ruby gem. -Alipay official document: https://b.alipay.com/order/techService.htm . +*This branch is not stable, use it at your own risk* ## Installation Add this line to your application's Gemfile: ```ruby -gem 'alipay', '~> 0.14.0' +gem 'alipay', github: 'chloerei/alipay', branch: 'openapi' ``` -And then execute: +Then run: ```console $ bundle ``` -## Configuration +## Usage ```ruby -Alipay.pid = 'YOUR_PID' -Alipay.key = 'YOUR_KEY' - -#Alipay.sign_type = 'MD5' # Available values: MD5, RSA. Default is MD5 -#Alipay.debug_mode = true # Enable parameter check. Default is true. -``` - -You can set default key, or pass a key directly to service method: - -```ruby -Service.create_partner_trade_by_buyer_url({ - out_trade_no: 'OUT_TRADE_NO', - # Order params... -}, { - pid: 'ANOTHER_PID', - key: 'ANOTHER_KEY', -}) -``` - -## Service - -### 担保交易收款接口 - -#### Name - -```ruby -create_partner_trade_by_buyer -``` - -#### Definition - -```ruby -Alipay::Service.create_partner_trade_by_buyer_url({ARGUMENTS}, {OPTIONS}) -``` - -#### Example - -```ruby -Alipay::Service.create_partner_trade_by_buyer_url( - out_trade_no: '20150401000-0001', - subject: 'Order Name', - price: '10.00', - quantity: 12, - logistics_type: 'DIRECT', - logistics_fee: '0', - logistics_payment: 'SELLER_PAY', - return_url: 'https://example.com/orders/20150401000-0001', - notify_url: 'https://example.com/orders/20150401000-0001/notify' -) -# => 'https://mapi.alipay.com/gateway.do?service=create_partner_trade_by_buyer&...' -``` - -Guide consumer to this address to complete payment - -#### Arguments - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| out_order_no | required | Order id in your application. | -| subject | required | Order subject. | -| price | required | Order item's price. | -| quantity | required | Order item's quantity, total price is price * quantity. | -| logistics_type | required | Logistics type. Available values: POST, EXPRESS, EMS, DIRECT. | -| logistics_fee | required | Logistics fee. | -| logistics_payment | required | Who pay the logistics fee. Available values: BUYER_PAY, SELLER_PAY, BUYER_PAY_AFTER_RECEIVE. | -| return_url | optional | Redirect customer to this url after payment. | -| notify_url | optional | Alipay asyn notify url. | - -This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/alipayescow.zip . - -### 确认发货接口 - -#### Name - -```ruby -send_goods_confirm_by_platform -``` - -#### Definition - -```ruby -Alipay::Service.send_goods_confirm_by_platform({ARGUMENTS}, {OPTIONS}) -``` - -#### Example - -```ruby -Alipay::Service.send_goods_confirm_by_platform( - trade_no: '201504010000001', - logistics_name: 'example.com', - transport_type: 'DIRECT' -) -# => 'T' -``` - -#### Arguments - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| trade_no | required | Trade number in Alipay system, should get from notify message. | -| logistics_name | required | Logistics Name. | -| transport_type/create_transport_type | required | Allowed values: POST, EXPRESS, EMS, DIRECT. | - -This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/alipayescow.zip . - -### 即时到账收款接口 - -#### Name - -```ruby -create_direct_pay_by_user -``` - -#### Definition - -```ruby -Alipay::Service.create_direct_pay_by_user_url({ARGUMENTS}, {OPTIONS}) -``` - -#### Example - -```ruby -Alipay::Service.create_direct_pay_by_user_url( - out_trade_no: '20150401000-0001', - subject: 'Order Name', - total_fee: '10.00', - return_url: 'https://example.com/orders/20150401000-0001', - notify_url: 'https://example.com/orders/20150401000-0001/notify' +alipay_client = Alipay::Client.new( + url: 'https://openapi.alipaydev.com/gateway.do', + app_id: '2016000000000000', + app_private_key: APP_PRIVATE_KEY, + alipay_public_key: ALIPAY_PUBLIC_KEY ) -``` - -#### Arguments - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| out_order_no | required | Order id in your application. | -| subject | required | Order subject. | -| total_fee | required | Order's total fee. | -| return_url | optional | Redirect customer to this url after payment. | -| notify_url | optional | Alipay asyn notify url. | - -This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/alipaydirect.zip . - -### 手机网站支付接口 - -#### Name - -```ruby -alipay.wap.create.direct.pay.by.user -``` -#### Definition - -```ruby -Alipay::Service.create_direct_pay_by_user_wap_url({ARGUMENTS}, {OPTIONS}) -``` - -#### Example - -```ruby -Alipay::Service.create_direct_pay_by_user_wap_url( - out_trade_no: '20150401000-0001', - subject: 'Order Name', - total_fee: '10.00', - return_url: 'https://example.com/orders/20150401000-0001', - notify_url: 'https://example.com/orders/20150401000-0001/notify' +alipay_client.page_execute_url( + method: 'alipay.trade.page.pay', + biz_content: { + out_trade_no: '20160401000000', + product_code: 'FAST_INSTANT_TRADE_PAY', + total_amount: '0.01', + subject: 'test' + }.to_json, # to_json is important! + timestamp: '2016-04-01 00:00:00' ) +# => 'https://openapi.alipaydev.com/gateway.do?app_id=201600...' ``` -#### Arguments - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| out_order_no | required | Order id in your application. | -| subject | required | Order subject. | -| total_fee | required | Order's total fee. | -| return_url | optional | Redirect customer to this url after payment. | -| notify_url | optional | Alipay asyn notify url. | - -This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/alipaywapdirect.zip . - - - - -### 国际支付宝移动接口 - -#### Name - -```ruby -create_forex_trade_wap -``` - -#### Definition - -```ruby -Alipay::Service.create_forex_trade_wap_url({ARGUMENTS}, {OPTIONS}) -``` - -#### Example +Read [Alipay::Client](lib/alipay/client.rb) for more detail. -```ruby -Alipay::Service.create_forex_trade_wap_url( - out_trade_no: '20150401000-0001', - subject: 'Order Name', - merchant_url: 'http://example.com/itemback', - total_fee: '10.00', #or rmb_fee, only one - currency: 'USD', - return_url: 'https://example.com/orders/20150401000-0001', - notify_url: 'https://example.com/orders/20150401000-0001/notify' -) -``` - -#### Arguments - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| out_order_no | required | Order id in your application. | -| subject | required | Order subject. | -| merchant_url | required | The link which customer could jump back to merchant page from Alipay cashier. | -| total_fee or rmb_fee | required | Order's total fee. | -| currency | required | currency type. | -| return_url | optional | Redirect customer to this url after payment. | -| notify_url | optional | Alipay asyn notify url. | - -This is not a complete list of arguments, please read official document: https://global.alipay.com/product/mobilepayments.html . - - -### 即时到账批量退款有密接口 - -#### Name - -```ruby -refund_fastpay_by_platform_pwd -``` - -#### Definition - -```ruby -Alipay::Service.refund_fastpay_by_platform_pwd_url -``` - -#### Example - -```ruby -batch_no = Alipay::Utils.generate_batch_no # refund batch no, you SHOULD store it to db to avoid alipay duplicate refund -Alipay::Service.refund_fastpay_by_platform_pwd_url( - batch_no: batch_no, - data: [{ - trade_no: '201504010000001', - amount: '10.0', - reason: 'REFUND_REASON' - }], - notify_url: 'https://example.com/orders/20150401000-0001/refund_notify' -) -# => https://mapi.alipay.com/gateway.do?service=refund_fastpay_by_platform_pwd&... -``` - -#### Arguments - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| batch_no | required | Refund batch no, you should store it to db to avoid alipay duplicate refund. | -| data | required | Refund data, a hash array. | -| notify_url | required | Alipay notify url. | - -##### Data Item - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| trade_no | required | Trade number in alipay system. | -| amount | required | Refund amount. | -| reason | required | Refund reason. Less than 256 bytes, could not contain special characters: ^ $ | #. | - -This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/alipaydirect.zip . - -### 关闭交易接口 - -#### Name - -```ruby -close_trade -``` - -#### Definition - -```ruby -Alipay::Service.close_trade({ARGUMENTS}, {OPTIONS}) -``` - -#### Example - -```ruby -Alipay::Service.close_trade( - trade_no: '201504010000001' -) -# => 'T' - -# When fail -# => 'F TRADE_STATUS_NOT_AVAILD' -``` - -#### ARGUMENTS - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| out_order_no | optional * | Order number in your application. | -| trade_no | optional * | Trade number in alipay system. | - -\* out_order_no and trade_no must have one. - -### 单笔交易查询接口 - -#### Name - -```ruby -single_trade_query -``` - -#### Definition - -```ruby -Alipay::Service.single_trade_query({ARGUMENTS}, {OPTIONS}) -``` - -#### Example - -```ruby -Alipay::Service.single_trade_query( - trade_no: '201504010000001' -) -# => 'T... -``` - -#### ARGUMENTS - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| out_trade_no | optional * | Order number in your application. | -| trade_no | optional * | Trade number in alipay system. | - -\* out_trade_no and trade_no must have one. - -### 境外收单接口 - -#### Name - -```ruby -create_forex_trade -``` - -#### Definition - -```ruby -Alipay::Service.create_forex_trade_url({ARGUMENTS}, {OPTIONS}) -``` - -#### Example - -```ruby -Alipay::Service.create_forex_trade_url( - out_trade_no: '20150401000-0001', - subject: 'Subject', - currency: 'USD', - total_fee: '10.00', - notify_url: 'https://example.com/orders/20150401000-0001/notify' -) -# => 'https://mapi.alipay.com/gateway.do?service=create_forex_trade...' -``` - -#### ARGUMENTS - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| out_trade_no | required | Order number in your application. | -| subject | required | Order subject. | -| currency | required | Abbreviated currency name. | -| total_fee | required * | Order total price. | -| notify_url | optional | Alipay asyn notify url. | - -\* total_fee can be replace by rmb_fee. - -### 境外收单单笔退款接口 - -#### Name - -```ruby -forex_refund -``` - -#### Definition - -```ruby -Alipay::Service.forex_refund_url({ARGUMENTS}, {OPTIONS}) -``` - -#### Example - -```ruby -Alipay::Service.forex_refund_url( - out_return_no: '20150401000-0001', - out_trade_no: '201504010000001', - return_amount: '10.00', - currency: 'USD', - reason: 'Reason', - gmt_return: '20150401000000' -) -``` - -#### ARGUMENTS - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| out_return_no | required | Refund no, you should store it to db to avoid alipay duplicate refund. | -| out_trade_no | required | Order number in your application. | -| return_amount | required | Refund amount. | -| currency | required | Abbreviated currency name. | -| reason | required | Refun reason. | -| gmt_return | required * | YYYYMMDDHHMMSS Beijing Time. | - -\* Auto set Time.now if not set. - -### 账单明细分页查询接口 - -#### Name - -```ruby -account.page.query -``` - -#### Definition - -```ruby -Alipay::Service::account_page_query({PARAMS}, {OPTIONS}) -``` - -#### Example - -```ruby -Alipay::Service.account_page_query( - page_no: 1, - gmt_start_time: '2015-10-25 00:00:00', - gmt_end_time: '2015-10-26 00:00:00' -) -``` - -#### Arguments - -It's an unpublic api, contact support for permission and document. - -### 批量付款到支付宝账户接口 - -#### Name - -```ruby -batch_trans_notify -``` - -#### Definition - -```ruby -Alipay::Service::batch_trans_notify_url({PARAMS}, {OPTIONS}) -``` - -#### Example - -```ruby -Alipay::Service.batch_trans_notify_url( - notify_url: 'https://example.com/orders/20150401000-0001/notify', - account_name: '毛毛', - detail_data: '0315006^testture0002@126.com^常炜买家^1000.00^hello', - batch_no: '20080107001', - batch_num: 1, - batch_fee: 1000.00, - email: 'biz_932@alitest.com' -) -#=> 'https://mapi.alipay.com/gateway.do?service=batch_trans_notify&...' -``` - -#### Arguments - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| notify_url | required | Alipay asyn notify url. | -| account_name | required | Alipay account name of payer. | -| detail_data | required | Payment data. | -| batch_no | required | Batch transaction number. | -| batch_num | required | Batch transaction count. | -| batch_fee | required | Batch transaction total amount. | -| email | required | Alipay email account of payer. | - -Document: https://doc.open.alipay.com/doc2/detail?treeId=64&articleId=103773&docType=1 - -### 验证通知 - -#### Name - -```ruby -notify_verify -``` - -#### Definition - -```ruby -Alipay::Notify.verify?({PARAMS}, {OPTIONS}) -``` - -#### Example - -```ruby -# Rails -# params except :controller_name, :action_name, :host, etc. -notify_params = params.except(*request.path_parameters.keys) - -Alipay::Notify.verify?(notify_params, options = {}) -``` - -## Mobile::Service - -### 移动支付接口 - -#### Name - -```ruby -mobile.securitypay.pay -``` - -#### Definition - -```ruby -Alipay::Mobile::Service.mobile_securitypay_pay_string({ARGUMENTS}, {OPTIONS}) -``` - -#### Example - -```ruby -Alipay::Mobile::Service.mobile_securitypay_pay_string( - out_trade_no: '20150401000-0001', - notify_url: 'https://example.com/orders/20150401000-0001/notify' - subject: 'subject', - total_fee: '10.00', - body: 'text' -) -# => service="mobile.securitypay.pay"&_input_charset="utf-8"&partner=... -``` - -#### ARGUMENTS - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| out_trade_no | required | Order number in your application. | -| notify_url | required | Alipay asyn notify url. | -| subject | required | Order subject. | -| total_fee | required | Order total price. | -| body | required | Order body, less than 512 bytes. | - -\* This service only support RSA sign_type. - -This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/WS_MOBILE_PAY_SDK_BASE.zip . - -### APP支付接口 - -#### Name - -```ruby -alipay.trade.app.pay -``` - -#### Definition - -```ruby -Alipay::App::Service.alipay_trade_app_pay({ARGUMENTS}, {OPTIONS}) -``` - -#### Example - -```ruby -biz_content = { - 'body': body, 'out_trade_no': out_trade_no, 'passback_params': passback_params, - 'product_code': 'QUICK_MSECURITY_PAY', 'subject': subject, 'total_amount': total_amount -}.to_json - -params = { - notify_url: 'https://example.com/orders/20150401000-0001/notify', - app_id: '1234567890', - biz_content: biz_content -} - -options = { - sign_type: :rsa2, # :rsa 或 :rsa2 - key: OpenSSL::PKey::RSA.new("private key content ...") -} - -Alipay::App::Service.alipay_trade_app_pay(params, options) -# => service="alipay.trade.app.pay"&_input_charset="utf-8"&partner=... -``` - -#### ARGUMENTS - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| app_id | required | Application-assigned id | -| out_trade_no | required | Order number in your application. | -| notify_url | required | Alipay asyn notify url. | -| subject | required | Order subject. | -| total_amount | required | Order total price. | -| body | required | Order body, less than 512 bytes. | -| biz_content | required | A list of business parameters | - -\* This service support RSA or RSA2 sign_type. - -This is not a complete list of arguments, please read official document: https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.vwh1xQ&treeId=193&articleId=105465&docType=1 . - -#### APP支付验证通知 - -```ruby -options = { - sign_type: :rsa2, # :rsa 或 :rsa2 - key: OpenSSL::PKey::RSA.new("alipay sdk public key ...") -} - -Alipay::App::Sign.verify?(params, options) -``` - -## Wap::Service - -### 授权接口 - -#### Name - -```ruby -alipay.wap.trade.create.direct -``` - -#### Definition - -```ruby -Alipay::Wap::Service.trade_create_direct_token({ARGUMENTS}, {OPTIONS}} -``` - -#### Example - -```ruby -token = Alipay::Wap::Service.trade_create_direct_token( - req_data: { - seller_account_name: 'account@example.com', - out_trade_no: '20150401000-0001', - subject: 'Subject', - total_fee: '10.0', - call_back_url: 'https://example.com/orders/20150401000-0001', - notify_url: 'https://example.com/orders/20150401000-0001/notify' - } -) -``` - -#### ARGUMENTS - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| req_data | required | See req_data ARGUMENTS | - -##### req_data ARGUMENTS - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| seller_account_name | required | Alipay seller account. | -| out_order_no | required | Order id in your application. | -| subject | required | Order subject. | -| total_fee | required | Order total price. | -| return_url | optional | Redirect customer to this url after payment. | -| notify_url | optional | Alipay asyn notify url. | - -This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/WS_WAP_PAYWAP.zip . - -### 交易接口 - -#### Name - -```ruby -alipay.wap.auth.authAndExecute -``` - -#### Definition - -```ruby -Alipay::Wap::Service.auth_and_execute_url({ARGUMENTS}, {OPTIONS}) -``` - -#### Example - -```ruby -Alipay::Wap::Service.auth_and_execute_url(request_token: token) -``` -#### ARGUMENTS - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| request_token | required | Get from trade_create_direct_token | - -### 风险探测服务接口 - -#### Name - -```ruby -alipay.security.risk.detect -``` - -#### Definition - -```ruby -Alipay::Wap::Service.security_risk_detect({ARGUMENTS}, {OPTIONS}) -``` - -#### Example - -```ruby -Alipay::Wap::Service.security_risk_detect({ - order_no: '1', - order_credate_time: '1970-01-01 00:00:00', - order_category: 'TestCase^AlipayGem^Ruby', - order_item_name: 'item', - order_amount: '0.01', - buyer_account_no: '2088123123', - buyer_bind_mobile: '13600000000', - buyer_reg_date: '1970-01-01 00:00:00', - terminal_type: 'WAP' -}, { - sign_type: 'RSA', - key: RSA_PRIVATE_KEY -}) -``` -#### ARGUMENTS - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| order_no | optional | Order id in your application. | -| order_credate_time | optional | Order created time. | -| order_category | optional | Categories of Order's items. using `^` as splitter. | -| order_item_name | optional | Order subject. | -| order_amount | optional | Order item's price. | -| buyer_account_no | optional | User id in your application. | -| buyer_reg_date | optional | User created time. | -| buyer_bind_mobile | optional | User mobile phone. | -| terminal_type | optional | The terminal type which user are using to request the payment, can be `MOBILE` for App, `WAP` for mobile, `WEB` for PC. | - -### 验证通知 - -#### Name - -```ruby -notify_verify -``` - -#### Definition - -```ruby -Alipay::Wap::Notify.verify?({PARAMS}, {OPTIONS}) -``` - -#### Example - -```ruby -# Rails -# params except :controller_name, :action_name, :host, etc. -notify_params = params.except(*request.path_parameters.keys) - -Alipay::Wap::Notify.verify?(notify_params) -``` +You can find legacy API document [here](doc/legacy_api.md). ## Contributing @@ -803,8 +59,6 @@ Bug report or pull request are welcome. Please write unit test with your code if necessary. -## Donate - -Donate to maintainer let him make this gem better. +## License -Alipay donate link: http://chloerei.com/donate/ . +MIT License diff --git a/doc/legacy_api.md b/doc/legacy_api.md new file mode 100644 index 0000000..d656fe8 --- /dev/null +++ b/doc/legacy_api.md @@ -0,0 +1,812 @@ +# Alipay Legacy API + +*It's recommended to use new alipay api - Open API, read [Alipay::Client](../lib/alipay/client.rb) for more detail.* + +A unofficial alipay ruby gem. + +Alipay official document: https://b.alipay.com/order/techService.htm . + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'alipay', '~> 0.14.0' +``` + +And then execute: + +```console +$ bundle +``` + +## Configuration + +```ruby +Alipay.pid = 'YOUR_PID' +Alipay.key = 'YOUR_KEY' + +#Alipay.sign_type = 'MD5' # Available values: MD5, RSA. Default is MD5 +#Alipay.debug_mode = true # Enable parameter check. Default is true. +``` + +You can set default key, or pass a key directly to service method: + +```ruby +Service.create_partner_trade_by_buyer_url({ + out_trade_no: 'OUT_TRADE_NO', + # Order params... +}, { + pid: 'ANOTHER_PID', + key: 'ANOTHER_KEY', +}) +``` + +## Service + +### 担保交易收款接口 + +#### Name + +```ruby +create_partner_trade_by_buyer +``` + +#### Definition + +```ruby +Alipay::Service.create_partner_trade_by_buyer_url({ARGUMENTS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Service.create_partner_trade_by_buyer_url( + out_trade_no: '20150401000-0001', + subject: 'Order Name', + price: '10.00', + quantity: 12, + logistics_type: 'DIRECT', + logistics_fee: '0', + logistics_payment: 'SELLER_PAY', + return_url: 'https://example.com/orders/20150401000-0001', + notify_url: 'https://example.com/orders/20150401000-0001/notify' +) +# => 'https://mapi.alipay.com/gateway.do?service=create_partner_trade_by_buyer&...' +``` + +Guide consumer to this address to complete payment + +#### Arguments + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| out_order_no | required | Order id in your application. | +| subject | required | Order subject. | +| price | required | Order item's price. | +| quantity | required | Order item's quantity, total price is price * quantity. | +| logistics_type | required | Logistics type. Available values: POST, EXPRESS, EMS, DIRECT. | +| logistics_fee | required | Logistics fee. | +| logistics_payment | required | Who pay the logistics fee. Available values: BUYER_PAY, SELLER_PAY, BUYER_PAY_AFTER_RECEIVE. | +| return_url | optional | Redirect customer to this url after payment. | +| notify_url | optional | Alipay asyn notify url. | + +This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/alipayescow.zip . + +### 确认发货接口 + +#### Name + +```ruby +send_goods_confirm_by_platform +``` + +#### Definition + +```ruby +Alipay::Service.send_goods_confirm_by_platform({ARGUMENTS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Service.send_goods_confirm_by_platform( + trade_no: '201504010000001', + logistics_name: 'example.com', + transport_type: 'DIRECT' +) +# => 'T' +``` + +#### Arguments + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| trade_no | required | Trade number in Alipay system, should get from notify message. | +| logistics_name | required | Logistics Name. | +| transport_type/create_transport_type | required | Allowed values: POST, EXPRESS, EMS, DIRECT. | + +This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/alipayescow.zip . + +### 即时到账收款接口 + +#### Name + +```ruby +create_direct_pay_by_user +``` + +#### Definition + +```ruby +Alipay::Service.create_direct_pay_by_user_url({ARGUMENTS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Service.create_direct_pay_by_user_url( + out_trade_no: '20150401000-0001', + subject: 'Order Name', + total_fee: '10.00', + return_url: 'https://example.com/orders/20150401000-0001', + notify_url: 'https://example.com/orders/20150401000-0001/notify' +) +``` + +#### Arguments + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| out_order_no | required | Order id in your application. | +| subject | required | Order subject. | +| total_fee | required | Order's total fee. | +| return_url | optional | Redirect customer to this url after payment. | +| notify_url | optional | Alipay asyn notify url. | + +This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/alipaydirect.zip . + +### 手机网站支付接口 + +#### Name + +```ruby +alipay.wap.create.direct.pay.by.user +``` + +#### Definition + +```ruby +Alipay::Service.create_direct_pay_by_user_wap_url({ARGUMENTS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Service.create_direct_pay_by_user_wap_url( + out_trade_no: '20150401000-0001', + subject: 'Order Name', + total_fee: '10.00', + return_url: 'https://example.com/orders/20150401000-0001', + notify_url: 'https://example.com/orders/20150401000-0001/notify' +) +``` + +#### Arguments + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| out_order_no | required | Order id in your application. | +| subject | required | Order subject. | +| total_fee | required | Order's total fee. | +| return_url | optional | Redirect customer to this url after payment. | +| notify_url | optional | Alipay asyn notify url. | + +This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/alipaywapdirect.zip . + + + + +### 国际支付宝移动接口 + +#### Name + +```ruby +create_forex_trade_wap +``` + +#### Definition + +```ruby +Alipay::Service.create_forex_trade_wap_url({ARGUMENTS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Service.create_forex_trade_wap_url( + out_trade_no: '20150401000-0001', + subject: 'Order Name', + merchant_url: 'http://example.com/itemback', + total_fee: '10.00', #or rmb_fee, only one + currency: 'USD', + return_url: 'https://example.com/orders/20150401000-0001', + notify_url: 'https://example.com/orders/20150401000-0001/notify' +) +``` + +#### Arguments + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| out_order_no | required | Order id in your application. | +| subject | required | Order subject. | +| merchant_url | required | The link which customer could jump back to merchant page from Alipay cashier. | +| total_fee or rmb_fee | required | Order's total fee. | +| currency | required | currency type. | +| return_url | optional | Redirect customer to this url after payment. | +| notify_url | optional | Alipay asyn notify url. | + +This is not a complete list of arguments, please read official document: https://global.alipay.com/product/mobilepayments.html . + + +### 即时到账批量退款有密接口 + +#### Name + +```ruby +refund_fastpay_by_platform_pwd +``` + +#### Definition + +```ruby +Alipay::Service.refund_fastpay_by_platform_pwd_url +``` + +#### Example + +```ruby +batch_no = Alipay::Utils.generate_batch_no # refund batch no, you SHOULD store it to db to avoid alipay duplicate refund +Alipay::Service.refund_fastpay_by_platform_pwd_url( + batch_no: batch_no, + data: [{ + trade_no: '201504010000001', + amount: '10.0', + reason: 'REFUND_REASON' + }], + notify_url: 'https://example.com/orders/20150401000-0001/refund_notify' +) +# => https://mapi.alipay.com/gateway.do?service=refund_fastpay_by_platform_pwd&... +``` + +#### Arguments + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| batch_no | required | Refund batch no, you should store it to db to avoid alipay duplicate refund. | +| data | required | Refund data, a hash array. | +| notify_url | required | Alipay notify url. | + +##### Data Item + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| trade_no | required | Trade number in alipay system. | +| amount | required | Refund amount. | +| reason | required | Refund reason. Less than 256 bytes, could not contain special characters: ^ $ | #. | + +This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/alipaydirect.zip . + +### 关闭交易接口 + +#### Name + +```ruby +close_trade +``` + +#### Definition + +```ruby +Alipay::Service.close_trade({ARGUMENTS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Service.close_trade( + trade_no: '201504010000001' +) +# => 'T' + +# When fail +# => 'F TRADE_STATUS_NOT_AVAILD' +``` + +#### ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| out_order_no | optional * | Order number in your application. | +| trade_no | optional * | Trade number in alipay system. | + +\* out_order_no and trade_no must have one. + +### 单笔交易查询接口 + +#### Name + +```ruby +single_trade_query +``` + +#### Definition + +```ruby +Alipay::Service.single_trade_query({ARGUMENTS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Service.single_trade_query( + trade_no: '201504010000001' +) +# => 'T... +``` + +#### ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| out_trade_no | optional * | Order number in your application. | +| trade_no | optional * | Trade number in alipay system. | + +\* out_trade_no and trade_no must have one. + +### 境外收单接口 + +#### Name + +```ruby +create_forex_trade +``` + +#### Definition + +```ruby +Alipay::Service.create_forex_trade_url({ARGUMENTS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Service.create_forex_trade_url( + out_trade_no: '20150401000-0001', + subject: 'Subject', + currency: 'USD', + total_fee: '10.00', + notify_url: 'https://example.com/orders/20150401000-0001/notify' +) +# => 'https://mapi.alipay.com/gateway.do?service=create_forex_trade...' +``` + +#### ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| out_trade_no | required | Order number in your application. | +| subject | required | Order subject. | +| currency | required | Abbreviated currency name. | +| total_fee | required * | Order total price. | +| notify_url | optional | Alipay asyn notify url. | + +\* total_fee can be replace by rmb_fee. + +### 境外收单单笔退款接口 + +#### Name + +```ruby +forex_refund +``` + +#### Definition + +```ruby +Alipay::Service.forex_refund_url({ARGUMENTS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Service.forex_refund_url( + out_return_no: '20150401000-0001', + out_trade_no: '201504010000001', + return_amount: '10.00', + currency: 'USD', + reason: 'Reason', + gmt_return: '20150401000000' +) +``` + +#### ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| out_return_no | required | Refund no, you should store it to db to avoid alipay duplicate refund. | +| out_trade_no | required | Order number in your application. | +| return_amount | required | Refund amount. | +| currency | required | Abbreviated currency name. | +| reason | required | Refun reason. | +| gmt_return | required * | YYYYMMDDHHMMSS Beijing Time. | + +\* Auto set Time.now if not set. + +### 账单明细分页查询接口 + +#### Name + +```ruby +account.page.query +``` + +#### Definition + +```ruby +Alipay::Service::account_page_query({PARAMS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Service.account_page_query( + page_no: 1, + gmt_start_time: '2015-10-25 00:00:00', + gmt_end_time: '2015-10-26 00:00:00' +) +``` + +#### Arguments + +It's an unpublic api, contact support for permission and document. + +### 批量付款到支付宝账户接口 + +#### Name + +```ruby +batch_trans_notify +``` + +#### Definition + +```ruby +Alipay::Service::batch_trans_notify_url({PARAMS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Service.batch_trans_notify_url( + notify_url: 'https://example.com/orders/20150401000-0001/notify', + account_name: '毛毛', + detail_data: '0315006^testture0002@126.com^常炜买家^1000.00^hello', + batch_no: '20080107001', + batch_num: 1, + batch_fee: 1000.00, + email: 'biz_932@alitest.com' +) +#=> 'https://mapi.alipay.com/gateway.do?service=batch_trans_notify&...' +``` + +#### Arguments + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| notify_url | required | Alipay asyn notify url. | +| account_name | required | Alipay account name of payer. | +| detail_data | required | Payment data. | +| batch_no | required | Batch transaction number. | +| batch_num | required | Batch transaction count. | +| batch_fee | required | Batch transaction total amount. | +| email | required | Alipay email account of payer. | + +Document: https://doc.open.alipay.com/doc2/detail?treeId=64&articleId=103773&docType=1 + +### 验证通知 + +#### Name + +```ruby +notify_verify +``` + +#### Definition + +```ruby +Alipay::Notify.verify?({PARAMS}, {OPTIONS}) +``` + +#### Example + +```ruby +# Rails +# params except :controller_name, :action_name, :host, etc. +notify_params = params.except(*request.path_parameters.keys) + +Alipay::Notify.verify?(notify_params, options = {}) +``` + +## Mobile::Service + +### 移动支付接口 + +#### Name + +```ruby +mobile.securitypay.pay +``` + +#### Definition + +```ruby +Alipay::Mobile::Service.mobile_securitypay_pay_string({ARGUMENTS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Mobile::Service.mobile_securitypay_pay_string( + out_trade_no: '20150401000-0001', + notify_url: 'https://example.com/orders/20150401000-0001/notify' + subject: 'subject', + total_fee: '10.00', + body: 'text' +) +# => service="mobile.securitypay.pay"&_input_charset="utf-8"&partner=... +``` + +#### ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| out_trade_no | required | Order number in your application. | +| notify_url | required | Alipay asyn notify url. | +| subject | required | Order subject. | +| total_fee | required | Order total price. | +| body | required | Order body, less than 512 bytes. | + +\* This service only support RSA sign_type. + +This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/WS_MOBILE_PAY_SDK_BASE.zip . + +### APP支付接口 + +#### Name + +```ruby +alipay.trade.app.pay +``` + +#### Definition + +```ruby +Alipay::App::Service.alipay_trade_app_pay({ARGUMENTS}, {OPTIONS}) +``` + +#### Example + +```ruby +biz_content = { + 'body': body, 'out_trade_no': out_trade_no, 'passback_params': passback_params, + 'product_code': 'QUICK_MSECURITY_PAY', 'subject': subject, 'total_amount': total_amount +}.to_json + +params = { + notify_url: 'https://example.com/orders/20150401000-0001/notify', + app_id: '1234567890', + biz_content: biz_content +} + +options = { + sign_type: :rsa2, # :rsa 或 :rsa2 + key: OpenSSL::PKey::RSA.new("private key content ...") +} + +Alipay::App::Service.alipay_trade_app_pay(params, options) +# => service="alipay.trade.app.pay"&_input_charset="utf-8"&partner=... +``` + +#### ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| app_id | required | Application-assigned id | +| out_trade_no | required | Order number in your application. | +| notify_url | required | Alipay asyn notify url. | +| subject | required | Order subject. | +| total_amount | required | Order total price. | +| body | required | Order body, less than 512 bytes. | +| biz_content | required | A list of business parameters | + +\* This service support RSA or RSA2 sign_type. + +This is not a complete list of arguments, please read official document: https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.vwh1xQ&treeId=193&articleId=105465&docType=1 . + +#### APP支付验证通知 + +```ruby +options = { + sign_type: :rsa2, # :rsa 或 :rsa2 + key: OpenSSL::PKey::RSA.new("alipay sdk public key ...") +} + +Alipay::App::Sign.verify?(params, options) +``` + +## Wap::Service + +### 授权接口 + +#### Name + +```ruby +alipay.wap.trade.create.direct +``` + +#### Definition + +```ruby +Alipay::Wap::Service.trade_create_direct_token({ARGUMENTS}, {OPTIONS}} +``` + +#### Example + +```ruby +token = Alipay::Wap::Service.trade_create_direct_token( + req_data: { + seller_account_name: 'account@example.com', + out_trade_no: '20150401000-0001', + subject: 'Subject', + total_fee: '10.0', + call_back_url: 'https://example.com/orders/20150401000-0001', + notify_url: 'https://example.com/orders/20150401000-0001/notify' + } +) +``` + +#### ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| req_data | required | See req_data ARGUMENTS | + +##### req_data ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| seller_account_name | required | Alipay seller account. | +| out_order_no | required | Order id in your application. | +| subject | required | Order subject. | +| total_fee | required | Order total price. | +| return_url | optional | Redirect customer to this url after payment. | +| notify_url | optional | Alipay asyn notify url. | + +This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/WS_WAP_PAYWAP.zip . + +### 交易接口 + +#### Name + +```ruby +alipay.wap.auth.authAndExecute +``` + +#### Definition + +```ruby +Alipay::Wap::Service.auth_and_execute_url({ARGUMENTS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Wap::Service.auth_and_execute_url(request_token: token) +``` +#### ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| request_token | required | Get from trade_create_direct_token | + +### 风险探测服务接口 + +#### Name + +```ruby +alipay.security.risk.detect +``` + +#### Definition + +```ruby +Alipay::Wap::Service.security_risk_detect({ARGUMENTS}, {OPTIONS}) +``` + +#### Example + +```ruby +Alipay::Wap::Service.security_risk_detect({ + order_no: '1', + order_credate_time: '1970-01-01 00:00:00', + order_category: 'TestCase^AlipayGem^Ruby', + order_item_name: 'item', + order_amount: '0.01', + buyer_account_no: '2088123123', + buyer_bind_mobile: '13600000000', + buyer_reg_date: '1970-01-01 00:00:00', + terminal_type: 'WAP' +}, { + sign_type: 'RSA', + key: RSA_PRIVATE_KEY +}) +``` +#### ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| order_no | optional | Order id in your application. | +| order_credate_time | optional | Order created time. | +| order_category | optional | Categories of Order's items. using `^` as splitter. | +| order_item_name | optional | Order subject. | +| order_amount | optional | Order item's price. | +| buyer_account_no | optional | User id in your application. | +| buyer_reg_date | optional | User created time. | +| buyer_bind_mobile | optional | User mobile phone. | +| terminal_type | optional | The terminal type which user are using to request the payment, can be `MOBILE` for App, `WAP` for mobile, `WEB` for PC. | + +### 验证通知 + +#### Name + +```ruby +notify_verify +``` + +#### Definition + +```ruby +Alipay::Wap::Notify.verify?({PARAMS}, {OPTIONS}) +``` + +#### Example + +```ruby +# Rails +# params except :controller_name, :action_name, :host, etc. +notify_params = params.except(*request.path_parameters.keys) + +Alipay::Wap::Notify.verify?(notify_params) +``` + +## Contributing + +Bug report or pull request are welcome. + +### Make a pull request + +1. Fork it +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create new Pull Request + +Please write unit test with your code if necessary. + +## Donate + +Donate to maintainer let him make this gem better. + +Alipay donate link: http://chloerei.com/donate/ . From d45cafd0c8a7ad61178a8c563e003da93ac0b789 Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 2 Jul 2017 20:16:46 +0800 Subject: [PATCH 13/56] Add alipay client document --- README.md | 4 +- lib/alipay/client.rb | 116 ++++++++++++++++++++++++++++++++++++- test/alipay/client_test.rb | 8 +-- 3 files changed, 121 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9c925a9..c54cb8f 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,9 @@ alipay_client.page_execute_url( # => 'https://openapi.alipaydev.com/gateway.do?app_id=201600...' ``` -Read [Alipay::Client](lib/alipay/client.rb) for more detail. +Read [Alipay::Client](lib/alipay/client.rb) for usage detail. + +Read [https://doc.open.alipay.com](https://doc.open.alipay.com) for Alipay API workflow and params. You can find legacy API document [here](doc/legacy_api.md). diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index ee3e7af..6979181 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -1,28 +1,104 @@ module Alipay class Client + # Create a client to manage all API request. + # + # Example: + # + # alipay_client = Alipay::Client.new( + # url: 'https://openapi.alipaydev.com/gateway.do', + # app_id: '2016000000000000', + # app_private_key: APP_PRIVATE_KEY, + # alipay_public_key: ALIPAY_PUBLIC_KEY + # ) + # + # Options: + # + # [:url] Alipay Open API gateway, + # 'https://openapi.alipaydev.com/gateway.do'(Sandbox) or + # 'https://openapi.alipay.com/gateway.do'(Production). + # + # [:app_id] Your APP ID. + # + # [:app_private_key] APP private key. + # + # [:alipay_public_key] Alipay public key. + # + # [:format] default is 'json', only support 'json'. + # + # [:charset] default is 'UTF-8', only support 'UTF-8'. + # + # [:sign_type] default is 'RSA2', support 'RSA2', 'RSA', 'RSA2' is recommended. def initialize(options) options = ::Alipay::Utils.stringify_keys(options) @url = options['url'] @app_id = options['app_id'] @app_private_key = options['app_private_key'] - @format = options['format'] || 'json' - @charset = options['charset'] || 'utf-8' @alipay_public_key = options['alipay_public_key'] + @format = options['format'] || 'json' + @charset = options['charset'] || 'UTF-8' @sign_type = options['sign_type'] || 'RSA2' end + # Generate a query string that use for APP SDK excute. + # + # Example: + # + # alipay_client.sdk_execute( + # method: 'alipay.trade.page.pay', + # biz_content: { + # out_trade_no: '20160401000000', + # product_code: 'QUICK_MSECURITY_PAY', + # total_amount: '0.01', + # subject: 'test' + # }.to_json, + # timestamp: '2016-04-01 00:00:00' + # ) + # # => 'app_id=2016000000000000&charset=utf-8&sig....' def sdk_execute(params) params = prepare_params(params) + URI.encode_www_form(params) end + # Generate a url that use to redirect user to Alipay payment page. + # + # Example: + # + # assert_equal url, @client.page_execute_url( + # method: 'alipay.trade.page.pay', + # biz_content: { + # out_trade_no: '20160401000000', + # product_code: 'FAST_INSTANT_TRADE_PAY', + # total_amount: '0.01', + # subject: 'test' + # }.to_json, + # timestamp: '2016-04-01 00:00:00' + # ) + # # => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' def page_execute_url(params) params = prepare_params(params) + uri = URI(@url) uri.query = URI.encode_www_form(params) uri.to_s end + # Generate a form string that use to render in view and auto POST to + # Alipay server. + # + # Example: + # + # assert_equal url, @client.page_execute_form( + # method: 'alipay.trade.page.pay', + # biz_content: { + # out_trade_no: '20160401000000', + # product_code: 'FAST_INSTANT_TRADE_PAY', + # total_amount: '0.01', + # subject: 'test' + # }.to_json, + # timestamp: '2016-04-01 00:00:00' + # ) + # # => '
'{ "alipay_data_dataservice_bill_downloadurl_query_response":{...' def execute(params) params = prepare_params(params) Net::HTTP.post_form(URI(@url), params).body end + # Generate sign for params. def sign(params) string = params.sort.map { |item| item.join('=') }.join('&') @@ -54,6 +143,29 @@ def sign(params) end end + # Verify Alipay notification. + # + # Example: + # + # params = { + # out_trade_no: '20160401000000', + # trade_status: 'TRADE_SUCCESS' + # sign: '...' + # } + # alipay_client.verify?(params) + # # => true / false + # + # Please notice that framework params (like Rails) contains path params, use + # request.query_parameters for GET notify or request.request_parameters for + # POST notify: + # + # def return_path + # alipay_client.verify?(request.query_parameters) + # end + # + # def notify_path + # alipay_client.verify?(request.request_parameters) + # end def verify?(params) params = Utils.stringify_keys(params) return false if params['sign_type'] != @sign_type diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index a0c9a7f..aa12c5d 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -7,7 +7,7 @@ def setup app_id: '2016000000000000', app_private_key: TEST_RSA_PRIVATE_KEY, format: 'json', - charset: 'utf-8', + charset: 'UTF-8', alipay_public_key: TEST_RSA_PUBLIC_KEY, sign_type: 'RSA2' ) @@ -18,7 +18,7 @@ def test_client_initialize end def test_sdk_execute_for_alipay_trade_app_pay - string = 'app_id=2016000000000000&charset=utf-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=Ws9J1PwRZx9Ku65rvRcbmy%2BhpfEpzeKculXbpa%2FuZbWfr4m2keutr63RQPhXqhWMKNGXh9BuzbX9khWZOa12YijxDljSW%2FCmtncsFoUpordhVVg6xGtjJqF28mRbx3uqmjxmThyRoa6taDKYB1qtAdoTfEnkfVG2zvR%2BZ6jg3dE%3D' + string = 'app_id=2016000000000000&charset=UTF-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=NbpUhRJ0HYSSIQGPxHOVLQpHcvzOyN8%2FeIhxq26Gglpx2%2BQvsJdffxg8bySiVcXryiZQbw1ZReZAO5HkokEtS8hiHp%2BNVnFPzZQLAiWXqHoO3hXkZTofjma618uhNfjFFYatgZX9Zio8XpSU9UaBL7sJiTqWY5cmDTKwbcc%2Ftck%3D' assert_equal string, @client.sdk_execute( method: 'alipay.trade.page.pay', @@ -33,7 +33,7 @@ def test_sdk_execute_for_alipay_trade_app_pay end def test_page_execute_url_for_alipay_trade_page_pay - url = 'https://openapi.alipaydev.com/gateway.do?app_id=2016000000000000&charset=utf-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22FAST_INSTANT_TRADE_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=Dyx7%2BjfeGF4qgBHjJt6i2mJ9brI6uamBeq%2BH6SLe44CjtphTpBq5HUeuvGI7Dhar%2FzgfMP0tzBCwEjb%2FKdwG3ZdcyB6jOWzwi5YX3fVjLx%2FsFY5qr%2Fng%2BCuEsuZ5QId%2FLGQnp04cT%2BefayR20qfMwmAjQVowBBy3KsKb7iYdn6k%3D' + url = 'https://openapi.alipaydev.com/gateway.do?app_id=2016000000000000&charset=UTF-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22FAST_INSTANT_TRADE_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=CXmicQH4t0AxsFGodhA3n824fKsJHH%2BAt2RsRvr1poYhhPpk4UUxd0Zrrv9ytNP28Ov1seNl0zZf%2FPfIYmg5ZquUHyFbYrXL3zDtJQntwRYBncZlKGkNDt0Bg0fMU2bJK3xr8TLW1FiJMb1sZ1a2PQaeeEG0PV0b7cf4KDsxIwQ%3D' assert_equal url, @client.page_execute_url( method: 'alipay.trade.page.pay', @@ -48,7 +48,7 @@ def test_page_execute_url_for_alipay_trade_page_pay end def test_page_execute_form_for_alipay_trade_page_pay - form = "
" + form = "
" assert_equal form, @client.page_execute_form( method: 'alipay.trade.page.pay', From 95a15160ac1636e2c0da39006ce872ec7efac13e Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 2 Jul 2017 20:26:57 +0800 Subject: [PATCH 14/56] Remmove Alipay::App module, it does not release --- doc/legacy_api.md | 64 --------------------------------- lib/alipay.rb | 2 -- lib/alipay/app/service.rb | 35 ------------------ lib/alipay/app/sign.rb | 36 ------------------- test/alipay/app/service_test.rb | 32 ----------------- test/alipay/app/sign_test.rb | 37 ------------------- 6 files changed, 206 deletions(-) delete mode 100644 lib/alipay/app/service.rb delete mode 100644 lib/alipay/app/sign.rb delete mode 100644 test/alipay/app/service_test.rb delete mode 100644 test/alipay/app/sign_test.rb diff --git a/doc/legacy_api.md b/doc/legacy_api.md index d656fe8..6c2b7c4 100644 --- a/doc/legacy_api.md +++ b/doc/legacy_api.md @@ -582,70 +582,6 @@ Alipay::Mobile::Service.mobile_securitypay_pay_string( This is not a complete list of arguments, please read official document: http://download.alipay.com/public/api/base/WS_MOBILE_PAY_SDK_BASE.zip . -### APP支付接口 - -#### Name - -```ruby -alipay.trade.app.pay -``` - -#### Definition - -```ruby -Alipay::App::Service.alipay_trade_app_pay({ARGUMENTS}, {OPTIONS}) -``` - -#### Example - -```ruby -biz_content = { - 'body': body, 'out_trade_no': out_trade_no, 'passback_params': passback_params, - 'product_code': 'QUICK_MSECURITY_PAY', 'subject': subject, 'total_amount': total_amount -}.to_json - -params = { - notify_url: 'https://example.com/orders/20150401000-0001/notify', - app_id: '1234567890', - biz_content: biz_content -} - -options = { - sign_type: :rsa2, # :rsa 或 :rsa2 - key: OpenSSL::PKey::RSA.new("private key content ...") -} - -Alipay::App::Service.alipay_trade_app_pay(params, options) -# => service="alipay.trade.app.pay"&_input_charset="utf-8"&partner=... -``` - -#### ARGUMENTS - -| Key | Requirement | Description | -| --- | ----------- | ----------- | -| app_id | required | Application-assigned id | -| out_trade_no | required | Order number in your application. | -| notify_url | required | Alipay asyn notify url. | -| subject | required | Order subject. | -| total_amount | required | Order total price. | -| body | required | Order body, less than 512 bytes. | -| biz_content | required | A list of business parameters | - -\* This service support RSA or RSA2 sign_type. - -This is not a complete list of arguments, please read official document: https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.vwh1xQ&treeId=193&articleId=105465&docType=1 . - -#### APP支付验证通知 - -```ruby -options = { - sign_type: :rsa2, # :rsa 或 :rsa2 - key: OpenSSL::PKey::RSA.new("alipay sdk public key ...") -} - -Alipay::App::Sign.verify?(params, options) -``` - ## Wap::Service ### 授权接口 diff --git a/lib/alipay.rb b/lib/alipay.rb index e4d0971..5eebf08 100644 --- a/lib/alipay.rb +++ b/lib/alipay.rb @@ -14,8 +14,6 @@ require 'alipay/wap/sign' require 'alipay/mobile/service' require 'alipay/mobile/sign' -require 'alipay/app/service' -require 'alipay/app/sign' require 'alipay/client' module Alipay diff --git a/lib/alipay/app/service.rb b/lib/alipay/app/service.rb deleted file mode 100644 index b13320b..0000000 --- a/lib/alipay/app/service.rb +++ /dev/null @@ -1,35 +0,0 @@ -module Alipay - module App - module Service - ALIPAY_TRADE_APP_PAY_REQUIRED_PARAMS = %w( app_id biz_content notify_url ) - - def self.alipay_trade_app_pay(params, options = {}) - params = Utils.stringify_keys(params) - Alipay::Service.check_required_params(params, ALIPAY_TRADE_APP_PAY_REQUIRED_PARAMS) - key = options[:key] || Alipay.key - - sign_type = (options[:sign_type] || :rsa2).to_s.upcase - - params = { - 'method' => 'alipay.trade.app.pay', - 'charset' => 'utf-8', - 'version' => '1.0', - 'timestamp' => Time.now.utc.strftime('%Y-%m-%d %H:%M:%S').to_s, - 'sign_type' => sign_type - }.merge(params) - - string = Alipay::App::Sign.params_to_sorted_string(params) - sign = case sign_type - when 'RSA' - ::Alipay::Sign::RSA.sign(key, string) - when 'RSA2' - ::Alipay::Sign::RSA2.sign(key, string) - else - raise ArgumentError, "invalid sign_type #{sign_type}, allow value: 'RSA', 'RSA2'" - end - - Alipay::App::Sign.params_to_encoded_string params.merge('sign' => sign) - end - end - end -end diff --git a/lib/alipay/app/sign.rb b/lib/alipay/app/sign.rb deleted file mode 100644 index 30325bc..0000000 --- a/lib/alipay/app/sign.rb +++ /dev/null @@ -1,36 +0,0 @@ -require 'erb' - -module Alipay - module App - module Sign - def self.verify?(params, options = {}) - params = ::Alipay::Utils.stringify_keys(params) - - sign_type = params.delete('sign_type').upcase - unless options[:sign_type].to_s.upcase == sign_type - raise "sign_type not match: params: #{params[:sign_type]} options: #{options[:sign_type]}" - end - - sign = params.delete('sign') - string = ::Alipay::Sign.params_to_string(params) - - case sign_type - when 'RSA' - ::Alipay::Sign::RSA.verify?(options[:key], string, sign) - when 'RSA2' - ::Alipay::Sign::RSA2.verify?(options[:key], string, sign) - else - false - end - end - - def self.params_to_sorted_string(params) - params.sort.map { |key, value| %Q(#{key}=#{value.to_s}) }.join('&') - end - - def self.params_to_encoded_string(params) - params.sort.map { |key, value| %Q(#{key}=#{ERB::Util.url_encode(value.to_s)}) }.join('&') - end - end - end -end diff --git a/test/alipay/app/service_test.rb b/test/alipay/app/service_test.rb deleted file mode 100644 index 13957c7..0000000 --- a/test/alipay/app/service_test.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'test_helper' - -class Alipay::Mobile::ServiceTest < Minitest::Test - def setup - @params = { - app_id: '2016000000000000', - biz_content: { - timeout_express: "30m", - seller_id: "", - product_code: "QUICK_MSECURITY_PAY", - total_amount: "0.01", - subject: "1", - body: "测试数据", - out_trade_no: "10000000" - }.to_json, - charset: 'utf-8', - format: 'json', - method: 'alipay.trade.app.pay', - notify_url: 'http://example.com/notify', - timestamp: '2016-08-25 20:26:31', - version: '1.0' - } - end - - def test_alipay_trade_app_pay_with_rsa - assert_equal 'app_id=2016000000000000&biz_content=%7B%22timeout_express%22%3A%2230m%22%2C%22seller_id%22%3A%22%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%221%22%2C%22body%22%3A%22%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE%22%2C%22out_trade_no%22%3A%2210000000%22%7D&charset=utf-8&format=json&method=alipay.trade.app.pay¬ify_url=http%3A%2F%2Fexample.com%2Fnotify&sign=Cvie7tAzUCH1JF7gVM2jMuEJ2%2FdHGWQesyl4MyV6MWnTeGzWhB4fgCJ%2B7EbdurHfiOVRpnGZkYc6twoWP53P2S6K4fp%2BazUJLVBgpSFy9ufn9Sf4BLpWMeOOEt5ufWdoWxWALhTbeEZnZReTDR1S%2FEXKw%2FTdjMWc3Q0kcG4ARzQ%3D&sign_type=RSA×tamp=2016-08-25%2020%3A26%3A31&version=1.0', Alipay::App::Service.alipay_trade_app_pay(@params, sign_type: 'RSA' , key: TEST_RSA_PRIVATE_KEY) - end - - def test_alipay_trade_app_pay_with_rsa2 - assert_equal 'app_id=2016000000000000&biz_content=%7B%22timeout_express%22%3A%2230m%22%2C%22seller_id%22%3A%22%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%221%22%2C%22body%22%3A%22%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE%22%2C%22out_trade_no%22%3A%2210000000%22%7D&charset=utf-8&format=json&method=alipay.trade.app.pay¬ify_url=http%3A%2F%2Fexample.com%2Fnotify&sign=fRBLfmUFXDRWW%2BtWD7BQlv8uymKU7Ky8TQKLF%2FnGkUsdCXnZ0MbqLFXAoE4Il6464n9YKncJhG50aDZ4s4HxpMMCku55so99nPkf3Uoksmje5Z5zSZusxnUHl5o7zf21%2BIOOf5mJzA%2Fp4BGZqFK00CoM07Ye9tSV%2Fid1hT8MbxA%3D&sign_type=RSA2×tamp=2016-08-25%2020%3A26%3A31&version=1.0', Alipay::App::Service.alipay_trade_app_pay(@params, sign_type: 'RSA2' , key: TEST_RSA_PRIVATE_KEY) - end -end diff --git a/test/alipay/app/sign_test.rb b/test/alipay/app/sign_test.rb deleted file mode 100644 index 7fe0e1b..0000000 --- a/test/alipay/app/sign_test.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'test_helper' -require 'base64' - -class Alipay::App::SignTest < Minitest::Test - def setup - @params = { - biz_content: {:name=>'顺道'}.to_json, - out_trade_no: 'MEM1234567', - total_amount: '0.01' - } - - end - - def test_params_to_sorted_string - assert_equal 'a=2&b=1', Alipay::App::Sign.params_to_sorted_string(b: 1, a: 2) - end - - def test_params_to_encoded_string - assert_equal 'biz_content=%7B%22name%22%3A%22%E9%A1%BA%E9%81%93%22%7D&out_trade_no=MEM1234567&total_amount=0.01', Alipay::App::Sign.params_to_encoded_string(@params) - end - - def test_verify_with_rsa2 - sign = 'c1R7jXEzg/gMzCEwqBcrYf1EliSQEVyC2JaCt9AUkRqpxIQGeRAjA18gid3/ubFQn0vWC8ajNB0veyy7g7wlbi/gM/6S3qZpkLP5n+vgLG5v84IHVIHeQIf7a/U3olW2fFg9IonEr8ohIMYZD5IB89j+supMvtyPHhWHVal2N9k=' - assert Alipay::App::Sign.verify?(@params.merge(sign: sign, sign_type: 'RSA2'), - sign_type: :rsa2, - key: TEST_RSA_PUBLIC_KEY - ) - end - - def test_verify_with_rsa - sign = 'l+vXVjKLNqtCMAHDgaLdWZaUkjyKEfp85K3GzRx7HJnxdxHhF4MHh2AJdnzLdnp653BnGtPs8SBC/25D2edZdho0+6q+d8RJw0ZiDmZAg7WRP5rq7nB1/SBaH6hLgp8HXL6Uqwo42Rik3I4Ecw9u7uK1spt/Ph4vzZJBq5QMc9I=' - assert Alipay::App::Sign.verify?(@params.merge(sign: sign, sign_type: 'RSA'), - sign_type: :rsa, - key: TEST_RSA_PUBLIC_KEY - ) - end -end From 4334e78c91718d9ed9b3e6cad29debd8dade267c Mon Sep 17 00:00:00 2001 From: Rei Date: Mon, 3 Jul 2017 01:02:23 +0800 Subject: [PATCH 15/56] fix client verify? --- lib/alipay/client.rb | 25 ++++++++----------------- test/alipay/client_test.rb | 15 +++++++++++++-- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index 6979181..d9ce571 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -131,7 +131,7 @@ def execute(params) # Generate sign for params. def sign(params) - string = params.sort.map { |item| item.join('=') }.join('&') + string = params_to_string(params) case @sign_type when 'RSA' @@ -154,33 +154,20 @@ def sign(params) # } # alipay_client.verify?(params) # # => true / false - # - # Please notice that framework params (like Rails) contains path params, use - # request.query_parameters for GET notify or request.request_parameters for - # POST notify: - # - # def return_path - # alipay_client.verify?(request.query_parameters) - # end - # - # def notify_path - # alipay_client.verify?(request.request_parameters) - # end def verify?(params) params = Utils.stringify_keys(params) return false if params['sign_type'] != @sign_type sign = params.delete('sign') - string = params.sort.map { |item| item.join('=') }.join('&') + string = params_to_string(params) case @sign_type when 'RSA' - ::Alipay::Sign::RSA.verify?(@app_private_key, string, sign) + ::Alipay::Sign::RSA.verify?(@alipay_public_key, string, sign) when 'RSA2' - ::Alipay::Sign::RSA2.verify?(@app_private_key, string, sign) + ::Alipay::Sign::RSA2.verify?(@alipay_public_key, string, sign) else raise "Unsupported sign_type: #{@sign_type}" end - Utils.secure_compare(sign(params), sign) end private @@ -196,5 +183,9 @@ def prepare_params(params) params['sign'] = sign(params) params end + + def params_to_string(params) + params.sort.map { |item| item.join('=') }.join('&') + end end end diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index aa12c5d..1702d92 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -89,9 +89,20 @@ def test_execute_for_data_dataservice_bill_downloadurl_query def test_verify params = { out_trade_no: '20160401000000', - trade_status: 'TRADE_SUCCESS' + trade_status: 'TRADE_SUCCESS', + sign_type: 'RSA2' } params[:sign] = @client.sign(params) - @client.verify?(params) + assert @client.verify?(params) + end + + def test_verify_when_wrong + params = { + out_trade_no: '20160401000000', + trade_status: 'TRADE_SUCCESS', + sign_type: 'RSA2', + sign: Base64.strict_encode64('WrongSign') + } + assert !@client.verify?(params) end end From 2d3c198643db4550e785e04145266d6cb6217cd6 Mon Sep 17 00:00:00 2001 From: Rei Date: Mon, 3 Jul 2017 01:08:09 +0800 Subject: [PATCH 16/56] Add sign_type in client verify comment --- lib/alipay/client.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index d9ce571..f9bed67 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -150,6 +150,7 @@ def sign(params) # params = { # out_trade_no: '20160401000000', # trade_status: 'TRADE_SUCCESS' + # sign_type: 'RSA2', # sign: '...' # } # alipay_client.verify?(params) From 3fa1d739afba618b326cc921204300400abe4856 Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 9 Jul 2017 21:00:24 +0800 Subject: [PATCH 17/56] fix client verify --- lib/alipay/client.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index f9bed67..968a613 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -160,6 +160,8 @@ def verify?(params) return false if params['sign_type'] != @sign_type sign = params.delete('sign') + # sign_type does not use in notify sign + params.delete('sign_type') string = params_to_string(params) case @sign_type when 'RSA' From 004dca3701fdbc0d07aee3cb72cfff69d8a90186 Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 9 Jul 2017 21:14:17 +0800 Subject: [PATCH 18/56] fix verify test --- test/alipay/client_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index 1702d92..7f1cfd2 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -89,10 +89,10 @@ def test_execute_for_data_dataservice_bill_downloadurl_query def test_verify params = { out_trade_no: '20160401000000', - trade_status: 'TRADE_SUCCESS', - sign_type: 'RSA2' + trade_status: 'TRADE_SUCCESS' } params[:sign] = @client.sign(params) + params[:sign_type] = 'RSA2' assert @client.verify?(params) end From ab3adb7afd676eb1149317789ed8349957d6fc63 Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 27 Aug 2017 19:44:52 +0800 Subject: [PATCH 19/56] Update version to 0.15.0 --- README.md | 10 ++++------ lib/alipay/version.rb | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c54cb8f..5d37614 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,16 @@ Unofficial alipay ruby gem. -*This branch is not stable, use it at your own risk* +Note: Alipay::Client Api have not enough feedback in production yet, please fully test in your staging environment before production. You can find legacy API document [here](doc/legacy_api.md). + +You should read [https://doc.open.alipay.com](https://doc.open.alipay.com) before using this gem. ## Installation Add this line to your application's Gemfile: ```ruby -gem 'alipay', github: 'chloerei/alipay', branch: 'openapi' +gem 'alipay', '~> 0.15.0' ``` Then run: @@ -43,10 +45,6 @@ alipay_client.page_execute_url( Read [Alipay::Client](lib/alipay/client.rb) for usage detail. -Read [https://doc.open.alipay.com](https://doc.open.alipay.com) for Alipay API workflow and params. - -You can find legacy API document [here](doc/legacy_api.md). - ## Contributing Bug report or pull request are welcome. diff --git a/lib/alipay/version.rb b/lib/alipay/version.rb index 94a63a9..26fff81 100644 --- a/lib/alipay/version.rb +++ b/lib/alipay/version.rb @@ -1,3 +1,3 @@ module Alipay - VERSION = "0.14.0" + VERSION = "0.15.0" end From 0bbb96b6c959f6c947d0645fe0f8a46c97c958fa Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 27 Aug 2017 19:54:22 +0800 Subject: [PATCH 20/56] Add changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6c521e..7e4a58f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## master +## v0.15.0 (2017-08-27) + +- Add `Alipay::Client` for open API. +- Stop adding new feature to Alipay::Service, Alipay::App::Service, Alipay::Wap::Service, Alipay::Mobile::Service. + ## v0.14.0 (2016-12-28) - Add `Alipay::App::Service.create_forex_trade_wap_url` method, thanks @xiaohesong #61 From 750f07509470007351f62d835bffc167e8095f77 Mon Sep 17 00:00:00 2001 From: Rei Date: Sun, 27 Aug 2017 20:18:16 +0800 Subject: [PATCH 21/56] fix document --- lib/alipay/client.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index 968a613..0e1b89d 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -64,7 +64,7 @@ def sdk_execute(params) # # Example: # - # assert_equal url, @client.page_execute_url( + # alipay_client.page_execute_url( # method: 'alipay.trade.page.pay', # biz_content: { # out_trade_no: '20160401000000', @@ -88,7 +88,7 @@ def page_execute_url(params) # # Example: # - # assert_equal url, @client.page_execute_form( + # alipay_client.page_execute_form( # method: 'alipay.trade.page.pay', # biz_content: { # out_trade_no: '20160401000000', @@ -115,7 +115,7 @@ def page_execute_form(params) # # Example: # - # @client.execute( + # alipay_client.execute( # method: 'alipay.data.dataservice.bill.downloadurl.query', # biz_content: { # bill_type: 'trade', From 26be7e3ea830e8996dc577f1a74962ae59f295b3 Mon Sep 17 00:00:00 2001 From: Ziyang Chen Date: Sat, 23 Sep 2017 15:02:47 +0800 Subject: [PATCH 22/56] Add quick start guide --- doc/quick_start_en.md | 570 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 570 insertions(+) create mode 100644 doc/quick_start_en.md diff --git a/doc/quick_start_en.md b/doc/quick_start_en.md new file mode 100644 index 0000000..822dc30 --- /dev/null +++ b/doc/quick_start_en.md @@ -0,0 +1,570 @@ +# Quick Start Guide + +Table of Contents +================= + +* [Client Setup](#client-setup) + * [Ruby](#ruby) + * [Ruby on Rails](#ruby-on-rails) +* [Create Payment](#create-payment) + * [Desktop](#desktop) + * [Mobile](#mobile) + * [QR Code](#qr-code) + * [Installment Plan](#installment-plan) + * [Verify Callback](#verify-callback) + * [Query Payment Status](#query-payment-status) +* [Stop Payment](#stop-payment) + * [Close Payment](#close-payment) + * [Cancel Payment](#cancel-payment) +* [Refund Payment](#refund-payment) + * [Initiate Refund](#initiate-refund) + * [Query Refund Status](#query-refund-status) +* [Fund Transfer](#fund-transfer) + * [Transfer Fund to Customer](#transfer-fund-to-customer) + * [Query Fund Transfer](#query-fund-transfer) + +## Client Setup + +**All examples in the quick start guide assume you have properly setup the client.** + +### Ruby +```ruby +# put your Alipay credentials here +API_URL: 'https://openapi.alipaydev.com/gateway.do' +APP_ID: '2016000000000000' +APP_PRIVATE_KEY: "-----BEGIN RSA PRIVATE KEY-----\nxkbt...4Wt7tl\n-----END RSA PRIVATE KEY-----\n" +ALIPAY_PUBLIC_KEY: "-----BEGIN PUBLIC KEY-----\nTq43T5...OVUAQb3R\n-----END PUBLIC KEY-----\n" + +# set up a client to talk to the Alipay API +@client = Alipay::Client.new( + url: API_URL, + app_id: APP_ID, + app_private_key: APP_PRIVATE_KEY, + alipay_public_key: ALIPAY_PUBLIC_KEY +) +``` + +### Ruby on Rails +You can save your Alipay credentials as environment variables or set them up using initializer. + +This guide will demonstrate setting up the Alipay client with the `dotenv-rails` gem. + +In your Gemfile +```ruby +# Gemfile +gem 'alipay', '~> 0.15.0' +gem 'dotenv-rails', '~> 2.2', '>= 2.2.1' +``` +Then run +```bash +$ bundle install +``` + +Create an .env file +```ruby +# .env +APP_ID=2016000000000000 +ALIPAY_API=https://openapi.alipaydev.com/gateway.do +APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nxkbt...4Wt7tl\n-----END RSA PRIVATE KEY-----\n" +ALIPAY_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nTq43T5...OVUAQb3R\n-----END PUBLIC KEY-----\n" +``` + +In your Ruby on Rails application, you can create an Alipay client instance like this: +```ruby +@client = Alipay::Client.new( + url: ENV['ALIPAY_API'], + app_id: ENV['APP_ID'], + app_private_key: ENV['APP_PRIVATE_KEY'], + alipay_public_key: ENV['ALIPAY_PUBLIC_KEY'] +) +``` + +## Create Payment + +### Desktop +#### API Method +``` +alipay.trade.page.pay +``` +This API method is for creating payment transaction that is suitable for customers using various desktop (PC) environment. + +#### Client Method +```ruby +Alipay::Client.page_execute_url +``` +This client method will generate a payment URL for redirecting customers to. + +#### Example +```ruby +@client.page_execute_url( + method: 'alipay.trade.page.pay', + return_url: 'https://mystore.com/orders/20160401000000/return', + notify_url: 'https://mystore.com/orders/20160401000000/notify', + biz_content: { + out_trade_no: '20160401000000', + product_code: 'FAST_INSTANT_TRADE_PAY', + total_amount: '0.01', + subject: 'Example #123' + }.to_json +) +# => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' +``` + +#### Notable Parameters +* `return_url` is where Alipay will redirect customers to after a successful payment is made. The redirect +will be sent via a `GET` request. This is an *optional* parameter. +* `notify_url` is where Alipay will send async callbacks to via `POST` request after a successful +payment. This is an *optional* parameter. +* `out_trade_no` is a unique string set by you. It is expected to be in reference to a model ID +in your application. Although it is not a strict requirement. +* `total_amount` should be in decimal form with a maximum scale of 2. For example `5123.99` will +be ¥,123.99 in text form. `5123.999` is not a valid parameter. + +> For a complete list of the available parameters, please refer to the [API documentation](https://docs.open.alipay.com/270/alipay.trade.page.pay/). + + +### Mobile +#### API Method +``` +alipay.trade.wap.pay +``` +This API method is for creating payment transaction for customers on a mobile device. It has the ability to transfer the payment process over to the native Alipay application if the application is installed on the customer's device. If not, the customer will be redirected to a mobile HTML version of the payment page. + +#### Client Method +```ruby +Alipay::Client.page_execute_url +``` +This method will generate a payment URL for redirecting customers to. + +#### Example +```ruby +@client.page_execute_url( + method: 'alipay.trade.wap.pay', + return_url: 'https://mystore.com/orders/20160401000000/return', + notify_url: 'https://mystore.com/orders/20160401000000/notify', + biz_content: { + out_trade_no: '20160401000000', + product_code: 'QUICK_WAP_WAY', + total_amount: '0.01', + subject: 'Example: 456' + quit_url: 'https://mystore.com/orders/20160401000000/' + }.to_json +) +# => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' +``` +#### Notebale Parameters +* `quit_url` is where Alipay will redirect customer to if the customer have completed the payment or have exited the payment process. This redirect only applies to the mobile html verison. This is an *optional* parameter. + +> For a complete list of the available parameters, please refer to the +[API documentation](https://docs.open.alipay.com/203/107090/). + +### QR Code +#### API Method +``` +alipay.trade.precreate +``` +This API method generates a payment URL that can be transformed into a QR code for customers to scan. A callback will be issued to the defined `notify_url` if payment is successful. + +#### Client Method +```ruby +Alipay::Client.execute +``` + +#### Example +```ruby +# Create a QR code based payment +response = @client.execute( + method: 'alipay.trade.precreate', + notify_url: 'https://mystore.com/orders/20160401000000/notify', + biz_content: { + out_trade_no: '20160401000000', + total_amount: '50.00', + subject: 'QR Code Test' + }.to_json +) +# => '{\"alipay_trade_precreate_response\":{\"code\"...' + +# Get payment url to render as QR code for customer +qr_code = JSON.parse(response)["alipay_trade_precreate_response"]["qr_code"] +# => 'https://qr.alipay.com/abcdefggfedcba' +``` + +> For a complete list of the available parameters, please refer to the [API documentation](https://docs.open.alipay.com/api_1/alipay.trade.precreate). + +### Installment Plan +#### API Methods +``` +alipay.trade.page.pay (Desktop / PC) +alipay.trade.wap.pay (Mobile) +alipay.trade.precreate (QR Code) +``` + +#### Client Method +```ruby +Alipay::Client.page_execute_url (Desktop / PC / Mobile) +Alipay::Client.execute (QR Code) +``` + +#### Example +Scenario: Customer pre-select a six-installment payment plan before going through the payment process at Alipay. + +```ruby +@client.page_execute_url( + method: 'alipay.trade.page.pay', + return_url: 'https://mystore.com/orders/20160401000000/return', + notify_url: 'https://mystore.com/orders/20160401000000/notify', + biz_content: { + out_trade_no: '20160401000000', + product_code: 'FAST_INSTANT_TRADE_PAY', + total_amount: '0.01', + subject: 'Example #654', + enable_pay_channels: 'balance,pcreditpayInstallment', + extend_params: { + hb_fq_num: '6'', + hb_fq_seller_percent: '0' + } + }.to_json +) +``` +Scenario: Customer select an installment plan or their choice at Alipay's payment page. +```ruby +@client.page_execute_url( + method: 'alipay.trade.page.pay', + return_url: 'https://mystore.com/orders/20160401000000/return', + notify_url: 'https://mystore.com/orders/20160401000000/notify', + biz_content: { + out_trade_no: '20160401000000', + product_code: 'FAST_INSTANT_TRADE_PAY', + total_amount: '0.01', + subject: 'Example #654', + enable_pay_channels: 'balance,pcreditpayInstallment', + }.to_json +) +``` +#### Notebale Parameters +* `enable_pay_channels` defines the funding methods that the customer can use to +pay for your order. `pcreditpayInstallment` is the value to enable installment plans. Mutiple +values can be enabled by sperating the values by `,` like the example above. +* `hb_fq_num` defines the number of installments. Valid values are `3`, `6`, or `12`. +* `hb_fq_seller_percent` defines the percentage of credit installment fee the seller is +responsiable for. Valid value is `0` where the customer bare all the fees. Another valid value is +`100` where you bare all the fees. However it only becomes valid if you have a special contract +signed with Alipay. + +> For a complete list of the available parameters, please refer to the [API documentation](https://docs.open.alipay.com/277/106748/). + + +### Verify Callback +#### Client Method +``` +Alipay::Client.verify? +``` +This client method will verify the validity of response params send by Alipay using Alipay's +public key. + +#### Example +```ruby +params = { + out_trade_no: '20160401000000', + trade_status: 'TRADE_SUCCESS' + sign_type: 'RSA2', + sign: '...' +} +@client.verify?(params) +# => true / false +``` + +##### Verify GET return_url +Alipay will send your customer via GET request to your specified `return_url`, if it was previously +defined during the payment creation process. Here is an example of how to verify response params +from Alipay in your controller. + +```ruby +@client.verify?(request.query_parameters) +# => true / false +``` + +##### Verify POST notify_url +Alipay will send POST request to your specified `notify_url`, if it was previously defined during +the payment creation process. Your controller action should response 'success' in plain text or +Alipay will keep on sending POST requests in increasing intervals. + +```ruby +if @client.verify?(request.request_parameters) + render plain: 'success' +end +``` + +### Query Payment Status +#### API Method +``` +alipay.trade.query +``` +This API method is for querying payment status after the payment is created. It suitable for +situations when callbacks fail or when the created payment did not set callback parameters. + +#### Client Method +```ruby +Alipay::Client.execute +``` + +#### Example +```ruby +response = @client.execute( + method: 'alipay.trade.query', + biz_content: { + trade_no: '2013112611001004680073956707', + }.to_json +) +# => '{\"alipay_trade_query_response\":{\"code\"...' + +# Get payment status +result_status = JSON.parse(response)["alipay_trade_query_response"]["trade_status"] +# => 'TRADE_SUCCESS' +``` + +Notable Parameters +`trade_no` is the payment identification string provided by Alipay via callback after the payment is +created. If you do not have this on hand, you can provide the `out_trade_no` instead. + +> For a complete list of the available parameters, please refer to the +[API documentation](https://docs.open.alipay.com/api_1/alipay.trade.query). + +## Stop Payment +### Close Payment +#### API Method +``` +alipay.trade.close +``` +This API method is for closing inactive payment if you don't want to wait for Alipay to close +out the payment at its default expiration time. + +#### Client Method +```ruby +Alipay::Client.execute +``` + +#### Example +```ruby +response = @client.execute( + method: 'alipay.trade.close', + notify_url: 'https://mystore.com/orders/20160401000000/notify', + biz_content: { + trade_no: '2013112611001004680073956707', + }.to_json +) +# => '{\"alipay_trade_close_response\":{\"code\"...' + +# Get request result +result_code = JSON.parse(response)["alipay_trade_close_response"]["code"] +# => '10000' +``` + +#### Notable Parameters +* `trade_no` is the payment identification that Alipay returned to you after the payment was +pre-created. If you do not have this parameter on hand, you can stub in the 'out_trade_no' that you +used using the payment creation process. + +> For a complete list of the available parameters, please refer to the +[API documentation](https://docs.open.alipay.com/api_1/alipay.trade.close). + +### Cancel Payment +#### API Method +``` +alipay.trade.cancel +``` +This API method is for canceling stuck payment or payment that errors out due to either Alipay +or your own application. If the customer has successfully made the payment. A refund will be made. +If customers had not pay yet, the payment transaction will be close. You can check if a refund +had been made by getting the `action` value from the request response. + +#### Client Method +```ruby +Alipay::Client.execute +``` + +#### Example +```ruby +response = @client.execute( + method: 'alipay.trade.cancel', + biz_content: { + out_trade_no: '20160401000000', + }.to_json +) +# => '{\"alipay_trade_cancel_response\":{\"code\"...' + +# Get cancellation result +result_action = JSON.parse(response)["alipay_trade_cancel_response"]["action"] +# => 'close' +``` + +#### Notable Parameters +* `out_trade_no` is a unique string that you provided during the payment creation process. You must +provide either `out_trade_no` or `trade_no` in order for the API call to be successful. + +> For a complete list of the available parameters, please refer to the +[API documentation](https://docs.open.alipay.com/api_1/alipay.trade.cancel/). + +## Refund Payment + +### Initiate Refund +#### API Method +``` +alipay.trade.refund +``` +This API method is for initiating refunds to customers. Only made this API call if the payment has not passed the refund period defined by Alipay. Multiple or partial refunds are also available through this API. + +#### Client Method +```ruby +Alipay::Client.execute +``` + +#### Example +Secenario: Customer request refund on a ¥10.12 item on a ¥210.85 order(payment). +```ruby +response = @client.execute( + method: 'alipay.trade.refund', + biz_content: { + out_trade_no: '6c50789a0610', + out_request_no: '6c50789a0610-1', + refund_amount: '10.12' + }.to_json +) +# => '{\"alipay_trade_refund_response\":{\"code\"...' + +# Get result +result_code = JSON.parse(response)["alipay_trade_refund_response"]["code"] +# => '10000' + +result_fund_change = JSON.parse(response)["alipay_trade_refund_response"]["fund_change"] +# => 'Y' +``` + +#### Notable Parameters +* `out_request_no` is an *conditional optional* parameter. It is *required* when you make a partial +refund or plan to make multiple refunds. It serves as identification for the refund transaction. +It is *optional* when you refund the entire payment amount. +* `refund_amount` is the amount you wish to refund to the customer. It can be lower than the original +payment amount. However, if you have previously issued partial refunds on a payment, the sum +of `refund_amount` cannot exceed the original payment amount. + +> For a complete list of the available parameters, please refer to the +[API documentation](https://docs.open.alipay.com/api_1/alipay.trade.refund/). + +### Query Refund Status +#### API Method +``` +alipay.trade.fastpay.refund.query +``` +This API method is for querying refund status. + +#### Client Method +```ruby +Alipay::Client.execute +``` + +#### Example +```ruby +response = @client.execute( + method: 'alipay.trade.fastpay.refund.query', + biz_content: { + out_trade_no: '6c50789a0610', + out_request_no: '6c50789a0610-1' + }.to_json +) +# => '{\"alipay_trade_fastpay_refund_query_response\":{\"code\"...' + +# Get refund amount +result_refund_amount = JSON.parse(response)["alipay_trade_fastpay_refund_query_response"]["refund_amount"] +# => '10.12' +``` + +#### Notable Parameters +* `out_request_no` is the identifying string provided by you provided when you initiated the refund. +If you did not provide this parameter when the refund was initiated, use the `out_trade_no` as your +`out_request_no`. + +> For a complete list of the available parameters, please refer to the +[API documentation](https://docs.open.alipay.com/api_1/alipay.trade.fastpay.refund.query/). + + +## Fund Transfer +### Transfer Fund to Customer +#### API Method +``` +alipay.fund.trans.toaccount.transfer +``` +This API method is for creating a fund transfer to customers from your Alipay account. + +#### Client Method +``` +Alipay::Client.execute +``` + +#### Example +```ruby +response = @client.execute( + method: 'alipay.fund.trans.toaccount.transfer', + biz_content: { + out_biz_no: '3142321423432', + payee_type: 'ALIPAY_LOGONID', + payee_account: 'customer@example.com', + amount: '12.23' + }.to_json +) +# => '{\"alipay_fund_trans_toaccount_transfer_response\":{\"code\"...' + +# Get order ID +result_order_id = JSON.parse(response)["alipay_fund_trans_toaccount_transfer_response"]["order_id"] +# => '20160627110070001502260006780837' +``` + +#### Notable Parameters +* `out_biz_no` is a unique identifying string provided by you for reference purposes. +* `payee_type` is for defining the type of `payee_account` that is provided. Valid values +are `ALIPAY_USERID` for customer's Alipay user ID starting with `2088` and `ALIPAY_LOGONID` for +customer cellular number or email address. +* `payee_account` is the customer Alipay ID, email or cellular number. It must match the `payee_type` +provided. +* `amount` is the amount you wish to transfer to the customer. (e.g. ¥123.50 is `123.50`) + +> For a complete list of the available parameters, please refer to the +[API documentation](https://docs.open.alipay.com/api_28/alipay.fund.trans.toaccount.transfer). + +### Query Fund Transfer +#### API Method +``` +alipay.fund.trans.order.query +``` +This API method is for querying fund transfer status. + +#### Client Method +``` +Alipay::Client.execute +``` + +#### Example +```ruby +response = @client.execute( + method: 'alipay.fund.trans.order.query', + biz_content: { + out_biz_no: '3142321423432', + }.to_json +) +# => '{\"alipay_fund_trans_order_query_response\":{\"code\"...' + +# Get refund_status +refund_status = JSON.parse(response)["alipay_fund_trans_order_query_response"]["status"] +# => 'SUCCESS' + +# Get expected refund date +refund_status = JSON.parse(response)["alipay_fund_trans_order_query_response"]["arrival_time_end"] +# => '2018-01-01 08:08:08' +``` + +#### Notable Parameters +* 'order_id' is the order id provided by Alipay when the transfer request was created. If you +do not have this on hand, you can stub in `out_biz_no` instead. + +> For a complete list of the available parameters, please refer to the +[API documentation](https://docs.open.alipay.com/api_28/alipay.fund.trans.order.query/). From 1f45cc840ffd346e2982c53313e317953a5f84e0 Mon Sep 17 00:00:00 2001 From: Ziyang Chen Date: Sat, 23 Sep 2017 21:45:47 +0800 Subject: [PATCH 23/56] Add guide on generating rsa key --- doc/rsa_key_en.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 doc/rsa_key_en.md diff --git a/doc/rsa_key_en.md b/doc/rsa_key_en.md new file mode 100644 index 0000000..11493b0 --- /dev/null +++ b/doc/rsa_key_en.md @@ -0,0 +1,77 @@ +# RSA Key for Alipay + +## Table of Contents + +* [Generate Application Key](#generate-application-key) + * [Generate RSA2 Keypair in Ruby](#generate-rsa2-keypair-in-ruby) + * [Saving Key](#saving-key) + * [Extract Key Content](#extract-key-content) +* [Signing Parameters](#signing-parameters) +* [Formatting the Public Key from Alipay](#formatting-the-public-key-from-alipay) + +### Generate Application Key +#### Generate RSA2 Keypair in Ruby +This example creates a 2048 bits RSA2 key. It is recommended by Alipay that +you use a RSA2 key. +```ruby +require 'openssl' + +@app_key = OpenSSL::PKey::RSA.new(2048) +``` +#### Saving Key +Saving Private Key to String +```ruby +app_private_key = @app_key.to_s +``` + +Saving Private Key to File +```ruby +open 'private_key.pem', 'w' do |io| io.write @app_key.to_pem end +``` + +Saving Public Key to String +```ruby +app_public_key = @app_key.public_key.to_s +``` + +Saving Public Key to File +```ruby +open 'public_key.pem', 'w' do |io| io.write @app_key.public_key.to_pem end +``` + +#### Extract Key Content +You will need to submit the application public key that you just created +to Alipay. However, you will need to strip the header, footer, and new line +characters from the key and just submit the key content to Alipay. +```ruby +key_content = app_public_key.gsub(/(-----BEGIN PUBLIC KEY-----)|(-----END PUBLIC KEY-----)|(\n)/, "") +puts key_content +# => 'MII0ey6QDZLB69i0e5Q0....' +``` + +### Signing Parameters +After you submit your application's public key to Alipay. There is an optional +step to validate the public key that you just uploaded by signing a parameter +provided by Alipay. + +```ruby +# validate params "a=123" +Base64.strict_encode64(@app_key.sign('sha256', "a=123")) +# => 'FokDu5uwgmNG2O/cb0QYD....' +``` + +### Formatting the Public Key from Alipay +The public key from Alipay does not contain any formatting. Ruby's OpenSSL +library cannot import/read the public key without proper formatting. To add +formatting back, run the following script. + +```ruby +pub_key = "MIIBI...HpwIDAQAB" +pub_key.tap do |key| + key.scan(/.{64}/).join("\n") + key.insert(0, "-----BEGIN PUBLIC KEY-----\n") + key.insert(-1, "\n-----END PUBLIC KEY-----\n") +end +# => "-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----\n" +``` + From d7d82aed3bf6825e9f8eccb15e05d2a9dc412a95 Mon Sep 17 00:00:00 2001 From: Ziyang Chen Date: Sat, 23 Sep 2017 22:08:23 +0800 Subject: [PATCH 24/56] Add a setup section and links to the rsa key guide and the quick start guide --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 5d37614..03e5f77 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,65 @@ # Alipay -Unofficial alipay ruby gem. +Unofficial Alipay ruby gem. -Note: Alipay::Client Api have not enough feedback in production yet, please fully test in your staging environment before production. You can find legacy API document [here](doc/legacy_api.md). +Alipay::Client API does not have enough feedback in production yet, +please fully test in your staging environment before production. +You can find legacy API document [here](doc/legacy_api.md). -You should read [https://doc.open.alipay.com](https://doc.open.alipay.com) before using this gem. +You should read [https://doc.open.alipay.com](https://doc.open.alipay.com) +before using this gem. ## Installation -Add this line to your application's Gemfile: +To install using [Bundler](http://bundler.io/). Add this line to your +application's Gemfile: ```ruby gem 'alipay', '~> 0.15.0' ``` Then run: - -```console +```bash $ bundle ``` -## Usage +Or you can manually install using [RubyGems](http://rubygems.org/): +```bash +$ gem install alipay -v 0.15.0 +``` + +## Getting Started + +This gem needs to be configured with your application's private key for Alipay +and Alipay's public key. Here is a [quick guide](doc/rsa_key_en.md) on +generating RSA key for use with this gem to get you started. +### Setup ```ruby -alipay_client = Alipay::Client.new( - url: 'https://openapi.alipaydev.com/gateway.do', - app_id: '2016000000000000', +require 'alipay' + +# setup the client to communicate with either production API or sandbox API +# https://openapi.alipay.com/gateway.do (Production) +# https://openapi.alipaydev.com/gateway.do (Sandbox) +API_URL = 'https://openapi.alipaydev.com/gateway.do' + +# setup your own credentials and certificates +APP_ID = '2016xxxxxxxxxxxx' +APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nxkbt...4Wt7tl\n-----END RSA PRIVATE KEY-----\n" +ALIPAY_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nTq43T5...OVUAQb3R\n-----END PUBLIC KEY-----\n" + +# initialize a client to communicate with the Alipay API +@alipay_client = Alipay::Client.new( + url: API_URL, + app_id: APP_ID app_private_key: APP_PRIVATE_KEY, alipay_public_key: ALIPAY_PUBLIC_KEY ) +``` -alipay_client.page_execute_url( +### Create a payment +```ruby +@alipay_client.page_execute_url( method: 'alipay.trade.page.pay', biz_content: { out_trade_no: '20160401000000', @@ -40,10 +69,13 @@ alipay_client.page_execute_url( }.to_json, # to_json is important! timestamp: '2016-04-01 00:00:00' ) + +# This method will then return a payment url # => 'https://openapi.alipaydev.com/gateway.do?app_id=201600...' ``` -Read [Alipay::Client](lib/alipay/client.rb) for usage detail. +Read [Alipay::Client](lib/alipay/client.rb) or +the [Quick Start Guide](doc/quick_start_en.md) for usage detail. ## Contributing From 941b7b6e67a13549295b72637e3828602d002545 Mon Sep 17 00:00:00 2001 From: Ziyang Chen Date: Sun, 24 Sep 2017 11:11:17 +0800 Subject: [PATCH 25/56] Add chinese version of the quick start guide --- doc/quick_start_cn.md | 532 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 532 insertions(+) create mode 100644 doc/quick_start_cn.md diff --git a/doc/quick_start_cn.md b/doc/quick_start_cn.md new file mode 100644 index 0000000..7aacc03 --- /dev/null +++ b/doc/quick_start_cn.md @@ -0,0 +1,532 @@ +简易入门指南 +================= + +### [English](quick_start_en.md) + +## 导航 +* [设置客户端](#设置客户端) + * [Ruby](#ruby) + * [Ruby on Rails](#ruby-on-rails) +* [创建支付订单](#创建支付订单) + * [电脑网站支付](#电脑网站支付) + * [手机网站支付](#手机网站支付) + * [扫码支付](#扫码支付) + * [分期付款](#分期付款) + * [验证回调数据](#验证回调数据) + * [查询支付状态](#查询支付状态) +* [终止支付订单](#终止支付订单) + * [关闭交易](#关闭交易) + * [撤销交易](#撤销交易) +* [交易退款](#交易退款) + * [发起退款](#发起退款) + * [查询退款状态](#查询退款状态) +* [转帐](#转帐) + * [转帐到顾客支付宝账户](#转帐到顾客支付宝账户) + * [查询转帐状态](#查询转帐状态) + + + +## 设置客户端 + +**指南内所有的示例会假设你已设置好客户端** + +### Ruby +```ruby +# 保存好应用ID,密钥,API接口地址等设置 +API_URL: 'https://openapi.alipaydev.com/gateway.do' +APP_ID: '2016000000000000' +APP_PRIVATE_KEY: "-----BEGIN RSA PRIVATE KEY-----\nxkbt...4Wt7tl\n-----END RSA PRIVATE KEY-----\n" +ALIPAY_PUBLIC_KEY: "-----BEGIN PUBLIC KEY-----\nTq43T5...OVUAQb3R\n-----END PUBLIC KEY-----\n" + +# 建立一个客户端以便快速调用API +@client = Alipay::Client.new( + url: API_URL, + app_id: APP_ID, + app_private_key: APP_PRIVATE_KEY, + alipay_public_key: ALIPAY_PUBLIC_KEY +) +``` + +### Ruby on Rails +你可以保存你的支付宝设置为环境变量,或者建立一个文档在Rails初始化时导入。 + +在这个范例里,我们将使用 `dotenv-rails` gem 把支付宝所需设置保存为环境变量. + +在你的 Gemfile +```ruby +# Gemfile +gem 'alipay', '~> 0.15.0' +gem 'dotenv-rails', '~> 2.2', '>= 2.2.1' +``` +命令行运行 +```bash +$ bundle install +``` + +Rails 根目录下创建一个 .env 文件 +```ruby +# .env +APP_ID=2016000000000000 +ALIPAY_API=https://openapi.alipaydev.com/gateway.do +APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nxkbt...4Wt7tl\n-----END RSA PRIVATE KEY-----\n" +ALIPAY_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nTq43T5...OVUAQb3R\n-----END PUBLIC KEY-----\n" +``` + +设后置好,你可以在你的 Ruby on Rails 应用里使用环境变量创建一个支付宝 API 客户端 +```ruby +@client = Alipay::Client.new( + url: ENV['ALIPAY_API'], + app_id: ENV['APP_ID'], + app_private_key: ENV['APP_PRIVATE_KEY'], + alipay_public_key: ENV['ALIPAY_PUBLIC_KEY'] +) +``` + +## 创建支付订单 + +### 电脑网站支付 +#### API 接口 +``` +alipay.trade.page.pay +``` +这个 API 接口主要是用于在顾客在电脑网站下单并发起支付。 + +#### 客户端函数 +```ruby +Alipay::Client.page_execute_url +``` +这个客户端函数调用后会返回一条重定向顾客用的支付地址。 + +#### 示例 +```ruby +@client.page_execute_url( + method: 'alipay.trade.page.pay', + return_url: 'https://mystore.com/orders/20160401000000/return', + notify_url: 'https://mystore.com/orders/20160401000000/notify', + biz_content: { + out_trade_no: '20160401000000', + product_code: 'FAST_INSTANT_TRADE_PAY', + total_amount: '0.01', + subject: 'Example #123' + }.to_json +) +# => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' +``` + +#### 值得注意的参数 +* `return_url` 是提供给支付宝的同步返回地址。客户支付成功后,支付宝会使用 GET 请求将用户重定向到这个地址。这个一个*选填*参数,可不提供。 +* `notify_url` 是提供给支付宝的异步返回地址。客户支付成功后,支付宝会以 POST 请求方式把交易信息发到这个地址上。这时一个*选填*参数。可不提供。 +* `out_trade_no` 是你作为商户提供给支付宝的订单号。建议引用你应用内相应模型的 ID,并不能重复。 +* `total_amount` 是支付订单的金额,精确到小数点后两位。例如金额 5,123.99 元的参数值应为 '5123.99'。 + +> 如需 API 接口的完整参数列表,请参考官方的 [API 文档](https://docs.open.alipay.com/270/alipay.trade.page.pay/). + + +### 手机网站支付 +#### API 接口 +``` +alipay.trade.wap.pay +``` +这个 API 接口适用于顾客在手机网站环境下创建支付订单。如顾客手机内装有支付宝钱包,重定向到这个接口将会唤起手机上支付宝钱包。如顾客未安装支付宝应用,则将会重新向到移动版支付页面。 + +#### 客户端函数 +```ruby +Alipay::Client.page_execute_url +``` +这个客户端函数调用后会返回一条重定向顾客用的支付地址。 + +#### 示例 +```ruby +@client.page_execute_url( + method: 'alipay.trade.wap.pay', + return_url: 'https://mystore.com/orders/20160401000000/return', + notify_url: 'https://mystore.com/orders/20160401000000/notify', + biz_content: { + out_trade_no: '20160401000000', + product_code: 'QUICK_WAP_WAY', + total_amount: '0.01', + subject: 'Example: 456' + quit_url: 'https://mystore.com/orders/20160401000000/' + }.to_json +) +# => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' +``` +#### 值得注意的参数 +* `quit_url` 顾客在移动版支付页面时,支付宝会以这个参数所提供的地址生成一个返回按钮。 + +> 如需 API 接口的完整参数列表,请参考官方的 [API 文档](https://docs.open.alipay.com/203/107090/). + +### 扫码支付 +#### API 接口 +``` +alipay.trade.precreate +``` +这个 API 接口在提供支付订单数据后,将会创建订单并返回一个生成二维码用的支付地址。支付成功后支付宝会向 `notify_url` 设定的异步通知地址发送交易数据。 + +#### 客户端函数 +```ruby +Alipay::Client.execute +``` + +#### 示例 +```ruby +# 创建支付订单并取得订单信息 +response = @client.execute( + method: 'alipay.trade.precreate', + notify_url: 'https://mystore.com/orders/20160401000000/notify', + biz_content: { + out_trade_no: '20160401000000', + total_amount: '50.00', + subject: 'QR Code Test' + }.to_json +) +# => '{\"alipay_trade_precreate_response\":{\"code\"...' + +# 提取二维码地址 +qr_code = JSON.parse(response)["alipay_trade_precreate_response"]["qr_code"] +# => 'https://qr.alipay.com/abcdefggfedcba' +``` + +> 如需 API 接口的完整参数列表,请参考官方的 [API 文档](https://docs.open.alipay.com/api_1/alipay.trade.precreate). + +### 分期付款 +#### API 接口 +``` +alipay.trade.page.pay (电脑网站) +alipay.trade.wap.pay (手机网站) +alipay.trade.precreate (扫码支付) +``` + +#### 客户端函数 +```ruby +Alipay::Client.page_execute_url (电脑网站 / 手机网站) +Alipay::Client.execute (扫码支付) +``` + +#### 示例 +情景:顾客在商城网站上预先选择好分6期付款的方案。商城和支付宝创建支付订单时同时提供分期参数。 + +```ruby +@client.page_execute_url( + method: 'alipay.trade.page.pay', + return_url: 'https://mystore.com/orders/20160401000000/return', + notify_url: 'https://mystore.com/orders/20160401000000/notify', + biz_content: { + out_trade_no: '20160401000000', + product_code: 'FAST_INSTANT_TRADE_PAY', + total_amount: '0.01', + subject: 'Example #654', + enable_pay_channels: 'balance,pcreditpayInstallment', + extend_params: { + hb_fq_num: '6'', + hb_fq_seller_percent: '0' + } + }.to_json +) +``` +情景:商城网站不提供分期选项,但允许客户在支付宝的支付过程中自行决定分期付款。 +```ruby +@client.page_execute_url( + method: 'alipay.trade.page.pay', + return_url: 'https://mystore.com/orders/20160401000000/return', + notify_url: 'https://mystore.com/orders/20160401000000/notify', + biz_content: { + out_trade_no: '20160401000000', + product_code: 'FAST_INSTANT_TRADE_PAY', + total_amount: '0.01', + subject: 'Example #654', + enable_pay_channels: 'balance,pcreditpayInstallment', + }.to_json +) +``` +#### 值得注意的参数 +* `enable_pay_channels` 这个参数指定用户可使用的付款渠道。 `pcreditpayInstallment` 是分期付款的参数值。可同时指定多个参数值。 +* `hb_fq_num` 这个参数指定分期数. 有效的参数值为 `3`,`6`, 和 `12`。 +* `hb_fq_seller_percent` 这个参数指定商户分担分期手续费的比例。有效参数值为`0`或`100`。 + +> 如需 API 接口的完整参数列表,请参考官方的 [API 文档](https://docs.open.alipay.com/277/106748/). + + +### 验证回调数据 +#### 客户端函数 +``` +Alipay::Client.verify? +``` +这个客户端函数将会使用支付宝所提供的公钥来验证回调数据。 + +#### 示例 +```ruby +params = { + out_trade_no: '20160401000000', + trade_status: 'TRADE_SUCCESS' + sign_type: 'RSA2', + sign: '...' +} +@client.verify?(params) +# => true / false +``` + +##### 验证 GET return_url 同步通知 +支付宝会在顾客支付成功后,将客户以 GET 请求的方式重定向顾客到你指定的 `return_url` 地址。以下将简单地示范如何在你应用的 Controller 里验证回调数据。 + +```ruby +@client.verify?(request.query_parameters) +# => true / false +``` + +##### 验证 POST notify_url 异步通知 +支付宝在顾客支付成功后,会向你所指定的 `notify_url` 以 POST 请求方式发送异步通知。你的应用对应的Controller需以纯文本方式返回 `success` 这7个字符。否则支付宝会继续尝试发送异步通知。 + +```ruby +if @client.verify?(request.request_parameters) + render plain: 'success' +end +``` + +### 查询支付状态 +#### API 接口 +``` +alipay.trade.query +``` +这个 API 接口适用于在未接收到同步/异步通知的情况下查询订单支付状态。这个接口同时也适用于查询未设定同步/异步通知地址的订单。 + +#### 客户端函数 +```ruby +Alipay::Client.execute +``` + +#### 示例 +```ruby +response = @client.execute( + method: 'alipay.trade.query', + biz_content: { + trade_no: '2013112611001004680073956707', + }.to_json +) +# => '{\"alipay_trade_query_response\":{\"code\"...' + +# Get payment status +result_status = JSON.parse(response)["alipay_trade_query_response"]["trade_status"] +# => 'TRADE_SUCCESS' +``` + +#### 值得注意的参数 +* `trade_no` 是创建支付订单后,支付宝返回的交易号。如未取得交易号,可以使用创建订单时所传入的商户订单号来代替。 + +> 如需 API 接口的完整参数列表,请参考官方的 [API 文档](https://docs.open.alipay.com/api_1/alipay.trade.query). + +## 终止支付订单 +### 关闭交易 +#### API 接口 +``` +alipay.trade.close +``` +这个 API 接口适用于用户在一定时间内未支付,但未在支付宝系统内超时的情况下对未付款的建议进行关闭。 + +#### 客户端函数 +```ruby +Alipay::Client.execute +``` + +#### 示例 +```ruby +response = @client.execute( + method: 'alipay.trade.close', + notify_url: 'https://mystore.com/orders/20160401000000/notify', + biz_content: { + trade_no: '2013112611001004680073956707', + }.to_json +) +# => '{\"alipay_trade_close_response\":{\"code\"...' + +# 取得请求结果代码 +result_code = JSON.parse(response)["alipay_trade_close_response"]["code"] +# => '10000' +``` + +#### 值得注意的参数 +* `trade_no` 是创建支付订单后,支付宝返回的交易号。如未取得交易号,可以使用创建订单时所传入的商户订单号来代替。 + +> 如需 API 接口的完整参数列表,请参考官方的 [API 文档](https://docs.open.alipay.com/api_1/alipay.trade.close). + +### 撤销交易 +#### API 接口 +``` +alipay.trade.cancel +``` +这个 API 接口适用于支付交易返回失败或支付系统超时的情况下撤销交易订单。如果顾客已成功付款,款项会以原渠道退回给顾客。并返回 `action` 值 `refund`。如客户尚未付款,订单会被关闭并返回 `action` 值 `close`。 + +#### 客户端函数 +```ruby +Alipay::Client.execute +``` + +#### 示例 +```ruby +response = @client.execute( + method: 'alipay.trade.cancel', + biz_content: { + out_trade_no: '20160401000000', + }.to_json +) +# => '{\"alipay_trade_cancel_response\":{\"code\"...' + +# 取得撤销结果 +result_action = JSON.parse(response)["alipay_trade_cancel_response"]["action"] +# => 'close' +``` + +#### 值得注意的参数 +* `trade_no` 是创建支付订单后,支付宝返回的交易号。如未取得交易号,可以使用创建订单时所传入的商户订单号来代替。 + +> 如需 API 接口的完整参数列表,请参考官方的 [API 文档](https://docs.open.alipay.com/api_1/alipay.trade.cancel/). + +## 交易退款 + +### 发起退款 +#### API 接口 +``` +alipay.trade.refund +``` +这个 API 接口适用于对已成功的交易订单发起退款。退款可拆分成多笔操作。如支付订单已超支付宝的退款期限,退款请求会失败。 + +#### 客户端函数 +```ruby +Alipay::Client.execute +``` + +#### 示例 +情景:顾客请求总额为 210.85 元的订单退款 10.12 元。 +```ruby +response = @client.execute( + method: 'alipay.trade.refund', + biz_content: { + out_trade_no: '6c50789a0610', + out_request_no: '6c50789a0610-1', + refund_amount: '10.12' + }.to_json +) +# => '{\"alipay_trade_refund_response\":{\"code\"...' + +# 取得结果 +result_code = JSON.parse(response)["alipay_trade_refund_response"]["code"] +# => '10000' + +result_fund_change = JSON.parse(response)["alipay_trade_refund_response"]["fund_change"] +# => 'Y' +``` + +#### 值得注意的参数 +* `out_request_no` 是 *条件可选* 参数. 如果你打算以同一张支付订单发起分多次退款,这则是*必选*参数。如你计划对支付订单进行一次性全额退款,这则是*可选*参数。 +* `refund_amount` 是你退款单的金额。该金额可小于支付订单金额。但如之前以同一支付订单已产生退款,所有退款单的总额不能超出支付订单的总额。 + +> 如需 API 接口的完整参数列表,请参考官方的 [API 文档](https://docs.open.alipay.com/api_1/alipay.trade.refund/). + +### 查询退款状态 +#### API 接口 +``` +alipay.trade.fastpay.refund.query +``` +这个 API 接口适用于查询退款状态和一些退款的参数。 + +#### 客户端函数 +```ruby +Alipay::Client.execute +``` + +#### 示例 +```ruby +response = @client.execute( + method: 'alipay.trade.fastpay.refund.query', + biz_content: { + out_trade_no: '6c50789a0610', + out_request_no: '6c50789a0610-1' + }.to_json +) +# => '{\"alipay_trade_fastpay_refund_query_response\":{\"code\"...' + +# 取得退款金额 +result_refund_amount = JSON.parse(response)["alipay_trade_fastpay_refund_query_response"]["refund_amount"] +# => '10.12' +``` + +#### 值得注意的参数 +* `out_request_no` 是你创建退款单时所提供给支付宝的单号。如支付订单仅有一笔退款,则可使用 `out_trade_no` 来代替。 + +> 如需 API 接口的完整参数列表,请参考官方的 [API 文档](https://docs.open.alipay.com/api_1/alipay.trade.fastpay.refund.query/). + + +## 转帐 +### 转帐到顾客支付宝账户 +#### API 接口 +``` +alipay.fund.trans.toaccount.transfer +``` +这个 API 接口适用于从你的商户支付宝账户转帐到顾客支付宝帐号。 + +#### 客户端函数 +``` +Alipay::Client.execute +``` + +#### 示例 +```ruby +response = @client.execute( + method: 'alipay.fund.trans.toaccount.transfer', + biz_content: { + out_biz_no: '3142321423432', + payee_type: 'ALIPAY_LOGONID', + payee_account: 'customer@example.com', + amount: '12.23' + }.to_json +) +# => '{\"alipay_fund_trans_toaccount_transfer_response\":{\"code\"...' + +# 取得转帐ID +result_order_id = JSON.parse(response)["alipay_fund_trans_toaccount_transfer_response"]["order_id"] +# => '20160627110070001502260006780837' +``` + +#### 值得注意的参数 +* `out_biz_no` 是你提供给支付宝的唯一转帐ID +* `payee_type` 是用于识别 `payee_account` 提供的账户类型。 有效值为 `ALIPAY_USERID` ,适用于提供开头为 `2088` 的支付宝用户号。另一有效值 `ALIPAY_LOGONID` 适用于以用户邮箱,手机,和登录号来识别。 +* `payee_account` 是用户的支付宝ID,邮箱地址,登录号,和手机号码。这个参数必须匹配所提供 `payee_type` 值。 +* `amount` 是转帐金额,精确到小数点后两位数。 (例如: ¥123.50 值为 `123.50`) + +> 如需 API 接口的完整参数列表,请参考官方的 [API 文档](https://docs.open.alipay.com/api_28/alipay.fund.trans.toaccount.transfer). + +### 查询转帐状态 +#### API 接口 +``` +alipay.fund.trans.order.query +``` +这个 API 接口适用于查询转帐状态 + +#### 客户端函数 +``` +Alipay::Client.execute +``` + +#### 示例 +```ruby +response = @client.execute( + method: 'alipay.fund.trans.order.query', + biz_content: { + out_biz_no: '3142321423432', + }.to_json +) +# => '{\"alipay_fund_trans_order_query_response\":{\"code\"...' + +# 取得转帐状态 +refund_status = JSON.parse(response)["alipay_fund_trans_order_query_response"]["status"] +# => 'SUCCESS' + +# 取得预计到帐时间 +refund_status = JSON.parse(response)["alipay_fund_trans_order_query_response"]["arrival_time_end"] +# => '2018-01-01 08:08:08' +``` + +#### 值得注意的参数 +* 'order_id' 是创建转帐交易时所返回的ID。如没有取得该ID,则可使用 `out_biz_no` 来代替。 + +> 如需 API 接口的完整参数列表,请参考官方的 [API 文档](https://docs.open.alipay.com/api_28/alipay.fund.trans.order.query/). From d55548ac70b8c20e0ec4a58393ce19593c28c045 Mon Sep 17 00:00:00 2001 From: Ziyang Chen Date: Sun, 24 Sep 2017 11:44:54 +0800 Subject: [PATCH 26/56] Add chinese version of the rsa key guide --- doc/rsa_key_cn.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 doc/rsa_key_cn.md diff --git a/doc/rsa_key_cn.md b/doc/rsa_key_cn.md new file mode 100644 index 0000000..4e7cc5c --- /dev/null +++ b/doc/rsa_key_cn.md @@ -0,0 +1,68 @@ +# 配合支付宝使用RSA密钥 + +[English](rsa_key_en.md) + +## 导航 +* [生成应用密钥](#生成应用密钥) +* [验证参数](#验证参数) +* [补充格式到支付宝公钥](#补充格式到支付宝公钥) + +### 生成应用密钥 +#### 在 Ruby 下生成 RSA2 密钥 +这个会示范在 Ruby 环境下生成RSA2密钥。支付宝推荐使用RSA2密钥来验证。 +```ruby +require 'openssl' + +@app_key = OpenSSL::PKey::RSA.new(2048) +``` +#### 保存密钥 +你可以使用以下任一方式来保存你的私钥和公钥 + +将私钥保存到字符串 +```ruby +app_private_key = @app_key.to_s +``` + +将私钥保存为证书文件 +```ruby +open 'private_key.pem', 'w' do |io| io.write @app_key.to_pem end +``` + +将公钥保存到字符串 +```ruby +app_public_key = @app_key.public_key.to_s +``` +将公钥保存为证书文件 +```ruby +open 'public_key.pem', 'w' do |io| io.write @app_key.public_key.to_pem end +``` + +#### 提取钥匙内容 +你需要给支付宝提供你所先前生成的公钥内容。但是提供给支付宝之前需要对 Ruby 生成的公钥进行格式清理。清理完后,将清理好的公钥内容提供给支付宝即可。 +```ruby +key_content = app_public_key.gsub(/(-----BEGIN PUBLIC KEY-----)|(-----END PUBLIC KEY-----)|(\n)/, "") +puts key_content +# => 'MII0ey6QDZLB69i0e5Q0....' +``` + +### 验证参数 +当你提交你的应用公钥给支付宝后,有一个可选的步骤是验证你的公钥的有效性。支付宝会提供一个参数让你使用你的私钥签名。把签名结果粘贴到支付宝后,支付宝会使用你上传的公钥来解密验证。 +```ruby +# validate params "a=123" +Base64.strict_encode64(@app_key.sign('sha256', "a=123")) +# => 'FokDu5uwgmNG2O/cb0QYD....' +``` + +### 补充格式到支付宝公钥 +你上传你的公钥后,支付宝会提供他们的公钥给你的应用来验证支付宝回调的内容有效性。但是他们提供公钥不带格式,所以 Ruby 的 OpneSSL 可能无法识别。将格式补充到支付宝所提供的公钥,你可以使用以下运行脚本。 + +```ruby +pub_key = "MIIBI...HpwIDAQAB" +pub_key.tap do |key| + key.scan(/.{64}/).join("\n") + key.insert(0, "-----BEGIN PUBLIC KEY-----\n") + key.insert(-1, "\n-----END PUBLIC KEY-----\n") +end +# => "-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----\n" +``` + From 365a8a55d0f1846a434a60600f7df23783e0cfce Mon Sep 17 00:00:00 2001 From: Ziyang Chen Date: Sun, 24 Sep 2017 11:52:57 +0800 Subject: [PATCH 27/56] Add link to its chinese version on each guide --- doc/quick_start_en.md | 11 ++++++----- doc/rsa_key_en.md | 4 ++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/doc/quick_start_en.md b/doc/quick_start_en.md index 822dc30..2d2c576 100644 --- a/doc/quick_start_en.md +++ b/doc/quick_start_en.md @@ -1,8 +1,9 @@ -# Quick Start Guide - -Table of Contents +Quick Start Guide ================= +### [中文](quick_start_cn.md) + +## Table of Contents * [Client Setup](#client-setup) * [Ruby](#ruby) * [Ruby on Rails](#ruby-on-rails) @@ -118,7 +119,7 @@ payment. This is an *optional* parameter. * `out_trade_no` is a unique string set by you. It is expected to be in reference to a model ID in your application. Although it is not a strict requirement. * `total_amount` should be in decimal form with a maximum scale of 2. For example `5123.99` will -be ¥,123.99 in text form. `5123.999` is not a valid parameter. +be ¥5,123.99 in text form. `5123.999` is not a valid parameter. > For a complete list of the available parameters, please refer to the [API documentation](https://docs.open.alipay.com/270/alipay.trade.page.pay/). @@ -324,7 +325,7 @@ result_status = JSON.parse(response)["alipay_trade_query_response"]["trade_statu ``` Notable Parameters -`trade_no` is the payment identification string provided by Alipay via callback after the payment is +* `trade_no` is the payment identification string provided by Alipay via callback after the payment is created. If you do not have this on hand, you can provide the `out_trade_no` instead. > For a complete list of the available parameters, please refer to the diff --git a/doc/rsa_key_en.md b/doc/rsa_key_en.md index 11493b0..ffbdc89 100644 --- a/doc/rsa_key_en.md +++ b/doc/rsa_key_en.md @@ -1,5 +1,7 @@ # RSA Key for Alipay +[中文](rsa_key_cn.md) + ## Table of Contents * [Generate Application Key](#generate-application-key) @@ -19,6 +21,8 @@ require 'openssl' @app_key = OpenSSL::PKey::RSA.new(2048) ``` #### Saving Key +You can save your private and public key as any of two formats. As long as it can be loaded into the program. + Saving Private Key to String ```ruby app_private_key = @app_key.to_s From 055f85ebaf24f792a7f0f89cd06adead5edf0cb3 Mon Sep 17 00:00:00 2001 From: Ziyang Chen Date: Sun, 24 Sep 2017 12:00:56 +0800 Subject: [PATCH 28/56] Remove new line characters from README.md --- README.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 03e5f77..385aae9 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,9 @@ Unofficial Alipay ruby gem. -Alipay::Client API does not have enough feedback in production yet, -please fully test in your staging environment before production. -You can find legacy API document [here](doc/legacy_api.md). +Note: Alipay::Client API does not have enough feedback in production yet, please fully test in your staging environment before production. You can find legacy API document [here](doc/legacy_api.md). -You should read [https://doc.open.alipay.com](https://doc.open.alipay.com) -before using this gem. +You should read [https://doc.open.alipay.com](https://doc.open.alipay.com) before using this gem. ## Installation @@ -30,9 +27,7 @@ $ gem install alipay -v 0.15.0 ## Getting Started -This gem needs to be configured with your application's private key for Alipay -and Alipay's public key. Here is a [quick guide](doc/rsa_key_en.md) on -generating RSA key for use with this gem to get you started. +This gem needs to be configured with your application's private key for Alipay and Alipay's public key. Here is a [quick guide](doc/rsa_key_en.md) on generating RSA key for use with this gem to get you started. ### Setup ```ruby @@ -74,8 +69,7 @@ ALIPAY_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nTq43T5...OVUAQb3R\n-----END PUBLI # => 'https://openapi.alipaydev.com/gateway.do?app_id=201600...' ``` -Read [Alipay::Client](lib/alipay/client.rb) or -the [Quick Start Guide](doc/quick_start_en.md) for usage detail. +Read [Alipay::Client](lib/alipay/client.rb) or the [Quick Start Guide](doc/quick_start_en.md) for usage detail. ## Contributing From 9c77e5b842038aecfcd4e80ac460b73ba779be1d Mon Sep 17 00:00:00 2001 From: Rei Date: Sat, 23 Dec 2017 16:17:51 +0800 Subject: [PATCH 29/56] Edit comment, fix #79 --- lib/alipay/client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index 0e1b89d..76aa905 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -44,7 +44,7 @@ def initialize(options) # Example: # # alipay_client.sdk_execute( - # method: 'alipay.trade.page.pay', + # method: 'alipay.trade.app.pay', # biz_content: { # out_trade_no: '20160401000000', # product_code: 'QUICK_MSECURITY_PAY', From 1a4638e9207538ba87a373e7fbd0ba86627108e0 Mon Sep 17 00:00:00 2001 From: tzzzoz Date: Wed, 3 Jan 2018 12:03:48 +0800 Subject: [PATCH 30/56] Bug fix for the formatting of the public key 1. A wrong regular expression was used, it ignored the last few characters whose length is less than 64. 2. #scan #join won't update the string itself, #insert wasn't applied on the correct string. --- doc/rsa_key_cn.md | 6 +----- doc/rsa_key_en.md | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/doc/rsa_key_cn.md b/doc/rsa_key_cn.md index 4e7cc5c..3f04fae 100644 --- a/doc/rsa_key_cn.md +++ b/doc/rsa_key_cn.md @@ -58,11 +58,7 @@ Base64.strict_encode64(@app_key.sign('sha256', "a=123")) ```ruby pub_key = "MIIBI...HpwIDAQAB" -pub_key.tap do |key| - key.scan(/.{64}/).join("\n") - key.insert(0, "-----BEGIN PUBLIC KEY-----\n") - key.insert(-1, "\n-----END PUBLIC KEY-----\n") -end +pub_key.scan(/.{64}|.+$/).join("\n").insert(0, "-----BEGIN PUBLIC KEY-----\n").insert(-1, "\n-----END PUBLIC KEY-----\n") # => "-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----\n" ``` diff --git a/doc/rsa_key_en.md b/doc/rsa_key_en.md index ffbdc89..623153b 100644 --- a/doc/rsa_key_en.md +++ b/doc/rsa_key_en.md @@ -71,11 +71,7 @@ formatting back, run the following script. ```ruby pub_key = "MIIBI...HpwIDAQAB" -pub_key.tap do |key| - key.scan(/.{64}/).join("\n") - key.insert(0, "-----BEGIN PUBLIC KEY-----\n") - key.insert(-1, "\n-----END PUBLIC KEY-----\n") -end +pub_key.scan(/.{64}|.+$/).join("\n").insert(0, "-----BEGIN PUBLIC KEY-----\n").insert(-1, "\n-----END PUBLIC KEY-----\n") # => "-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----\n" ``` From a99fc7cf0101c5dda3c6e28b0566d47a4dba5a5e Mon Sep 17 00:00:00 2001 From: Rei Date: Sat, 3 Mar 2018 19:12:28 +0800 Subject: [PATCH 31/56] Edit document, fix json encoding --- README.md | 2 +- doc/quick_start_cn.md | 24 ++++++++++++------------ doc/quick_start_en.md | 24 ++++++++++++------------ lib/alipay/client.rb | 8 ++++---- test/alipay/client_test.rb | 8 ++++---- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 385aae9..913c248 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ ALIPAY_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nTq43T5...OVUAQb3R\n-----END PUBLI product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' - }.to_json, # to_json is important! + }.to_json(ascii_only: true), # to_json(ascii_only: true) is important! timestamp: '2016-04-01 00:00:00' ) diff --git a/doc/quick_start_cn.md b/doc/quick_start_cn.md index 7aacc03..b60f85e 100644 --- a/doc/quick_start_cn.md +++ b/doc/quick_start_cn.md @@ -108,7 +108,7 @@ Alipay::Client.page_execute_url product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'Example #123' - }.to_json + }.to_json(ascii_only: true) ) # => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' ``` @@ -147,7 +147,7 @@ Alipay::Client.page_execute_url total_amount: '0.01', subject: 'Example: 456' quit_url: 'https://mystore.com/orders/20160401000000/' - }.to_json + }.to_json(ascii_only: true) ) # => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' ``` @@ -178,7 +178,7 @@ response = @client.execute( out_trade_no: '20160401000000', total_amount: '50.00', subject: 'QR Code Test' - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_trade_precreate_response\":{\"code\"...' @@ -221,7 +221,7 @@ Alipay::Client.execute (扫码支付) hb_fq_num: '6'', hb_fq_seller_percent: '0' } - }.to_json + }.to_json(ascii_only: true) ) ``` 情景:商城网站不提供分期选项,但允许客户在支付宝的支付过程中自行决定分期付款。 @@ -236,7 +236,7 @@ Alipay::Client.execute (扫码支付) total_amount: '0.01', subject: 'Example #654', enable_pay_channels: 'balance,pcreditpayInstallment', - }.to_json + }.to_json(ascii_only: true) ) ``` #### 值得注意的参数 @@ -301,7 +301,7 @@ response = @client.execute( method: 'alipay.trade.query', biz_content: { trade_no: '2013112611001004680073956707', - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_trade_query_response\":{\"code\"...' @@ -335,7 +335,7 @@ response = @client.execute( notify_url: 'https://mystore.com/orders/20160401000000/notify', biz_content: { trade_no: '2013112611001004680073956707', - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_trade_close_response\":{\"code\"...' @@ -367,7 +367,7 @@ response = @client.execute( method: 'alipay.trade.cancel', biz_content: { out_trade_no: '20160401000000', - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_trade_cancel_response\":{\"code\"...' @@ -404,7 +404,7 @@ response = @client.execute( out_trade_no: '6c50789a0610', out_request_no: '6c50789a0610-1', refund_amount: '10.12' - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_trade_refund_response\":{\"code\"...' @@ -441,7 +441,7 @@ response = @client.execute( biz_content: { out_trade_no: '6c50789a0610', out_request_no: '6c50789a0610-1' - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_trade_fastpay_refund_query_response\":{\"code\"...' @@ -478,7 +478,7 @@ response = @client.execute( payee_type: 'ALIPAY_LOGONID', payee_account: 'customer@example.com', amount: '12.23' - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_fund_trans_toaccount_transfer_response\":{\"code\"...' @@ -513,7 +513,7 @@ response = @client.execute( method: 'alipay.fund.trans.order.query', biz_content: { out_biz_no: '3142321423432', - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_fund_trans_order_query_response\":{\"code\"...' diff --git a/doc/quick_start_en.md b/doc/quick_start_en.md index 2d2c576..17e0aaf 100644 --- a/doc/quick_start_en.md +++ b/doc/quick_start_en.md @@ -106,7 +106,7 @@ This client method will generate a payment URL for redirecting customers to. product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'Example #123' - }.to_json + }.to_json(ascii_only: true) ) # => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' ``` @@ -149,7 +149,7 @@ This method will generate a payment URL for redirecting customers to. total_amount: '0.01', subject: 'Example: 456' quit_url: 'https://mystore.com/orders/20160401000000/' - }.to_json + }.to_json(ascii_only: true) ) # => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' ``` @@ -181,7 +181,7 @@ response = @client.execute( out_trade_no: '20160401000000', total_amount: '50.00', subject: 'QR Code Test' - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_trade_precreate_response\":{\"code\"...' @@ -224,7 +224,7 @@ Scenario: Customer pre-select a six-installment payment plan before going throug hb_fq_num: '6'', hb_fq_seller_percent: '0' } - }.to_json + }.to_json(ascii_only: true) ) ``` Scenario: Customer select an installment plan or their choice at Alipay's payment page. @@ -239,7 +239,7 @@ Scenario: Customer select an installment plan or their choice at Alipay's paymen total_amount: '0.01', subject: 'Example #654', enable_pay_channels: 'balance,pcreditpayInstallment', - }.to_json + }.to_json(ascii_only: true) ) ``` #### Notebale Parameters @@ -315,7 +315,7 @@ response = @client.execute( method: 'alipay.trade.query', biz_content: { trade_no: '2013112611001004680073956707', - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_trade_query_response\":{\"code\"...' @@ -352,7 +352,7 @@ response = @client.execute( notify_url: 'https://mystore.com/orders/20160401000000/notify', biz_content: { trade_no: '2013112611001004680073956707', - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_trade_close_response\":{\"code\"...' @@ -390,7 +390,7 @@ response = @client.execute( method: 'alipay.trade.cancel', biz_content: { out_trade_no: '20160401000000', - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_trade_cancel_response\":{\"code\"...' @@ -429,7 +429,7 @@ response = @client.execute( out_trade_no: '6c50789a0610', out_request_no: '6c50789a0610-1', refund_amount: '10.12' - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_trade_refund_response\":{\"code\"...' @@ -471,7 +471,7 @@ response = @client.execute( biz_content: { out_trade_no: '6c50789a0610', out_request_no: '6c50789a0610-1' - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_trade_fastpay_refund_query_response\":{\"code\"...' @@ -511,7 +511,7 @@ response = @client.execute( payee_type: 'ALIPAY_LOGONID', payee_account: 'customer@example.com', amount: '12.23' - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_fund_trans_toaccount_transfer_response\":{\"code\"...' @@ -550,7 +550,7 @@ response = @client.execute( method: 'alipay.fund.trans.order.query', biz_content: { out_biz_no: '3142321423432', - }.to_json + }.to_json(ascii_only: true) ) # => '{\"alipay_fund_trans_order_query_response\":{\"code\"...' diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index 76aa905..ccea6a3 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -50,7 +50,7 @@ def initialize(options) # product_code: 'QUICK_MSECURITY_PAY', # total_amount: '0.01', # subject: 'test' - # }.to_json, + # }.to_json(ascii_only: true), # timestamp: '2016-04-01 00:00:00' # ) # # => 'app_id=2016000000000000&charset=utf-8&sig....' @@ -71,7 +71,7 @@ def sdk_execute(params) # product_code: 'FAST_INSTANT_TRADE_PAY', # total_amount: '0.01', # subject: 'test' - # }.to_json, + # }.to_json(ascii_only: true), # timestamp: '2016-04-01 00:00:00' # ) # # => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' @@ -95,7 +95,7 @@ def page_execute_url(params) # product_code: 'FAST_INSTANT_TRADE_PAY', # total_amount: '0.01', # subject: 'test' - # }.to_json, + # }.to_json(ascii_only: true), # timestamp: '2016-04-01 00:00:00' # ) # # => '
'{ "alipay_data_dataservice_bill_downloadurl_query_response":{...' def execute(params) diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index 7f1cfd2..2c6d977 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -27,7 +27,7 @@ def test_sdk_execute_for_alipay_trade_app_pay product_code: 'QUICK_MSECURITY_PAY', total_amount: '0.01', subject: 'test' - }.to_json, + }.to_json(ascii_only: true), timestamp: '2016-04-01 00:00:00' ) end @@ -42,7 +42,7 @@ def test_page_execute_url_for_alipay_trade_page_pay product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' - }.to_json, + }.to_json(ascii_only: true), timestamp: '2016-04-01 00:00:00' ) end @@ -57,7 +57,7 @@ def test_page_execute_form_for_alipay_trade_page_pay product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' - }.to_json, + }.to_json(ascii_only: true), timestamp: '2016-04-01 00:00:00' ) end @@ -81,7 +81,7 @@ def test_execute_for_data_dataservice_bill_downloadurl_query biz_content: { bill_type: 'trade', bill_date: '2016-04-01' - }.to_json + }.to_json(ascii_only: true) ) end From 904aba7e235c9d131545653c6271454e1d652da2 Mon Sep 17 00:00:00 2001 From: Grayson Chen Date: Thu, 29 Mar 2018 22:47:32 +0800 Subject: [PATCH 32/56] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 913c248..6ec3b09 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ ALIPAY_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nTq43T5...OVUAQb3R\n-----END PUBLI # initialize a client to communicate with the Alipay API @alipay_client = Alipay::Client.new( url: API_URL, - app_id: APP_ID + app_id: APP_ID, app_private_key: APP_PRIVATE_KEY, alipay_public_key: ALIPAY_PUBLIC_KEY ) From 1fe4536945ba0dc23801bbb1d33f71af9128e019 Mon Sep 17 00:00:00 2001 From: y-macpro Date: Thu, 14 Jun 2018 22:04:59 +0800 Subject: [PATCH 33/56] support set seller_id outside --- lib/alipay/mobile/service.rb | 4 ++-- lib/alipay/service.rb | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/alipay/mobile/service.rb b/lib/alipay/mobile/service.rb index b7d546b..d886dda 100644 --- a/lib/alipay/mobile/service.rb +++ b/lib/alipay/mobile/service.rb @@ -12,8 +12,8 @@ def self.mobile_securitypay_pay_string(params, options = {}) params = { 'service' => 'mobile.securitypay.pay', '_input_charset' => 'utf-8', - 'partner' => options[:pid] || Alipay.pid, - 'seller_id' => options[:pid] || Alipay.pid, + 'partner' => options[:partner] || options[:pid] || Alipay.pid, + 'seller_id' => options[:seller_id] || options[:pid] || Alipay.pid, 'payment_type' => '1' }.merge(params) diff --git a/lib/alipay/service.rb b/lib/alipay/service.rb index ed027ab..21ca205 100644 --- a/lib/alipay/service.rb +++ b/lib/alipay/service.rb @@ -10,8 +10,8 @@ def self.create_partner_trade_by_buyer_url(params, options = {}) params = { 'service' => 'create_partner_trade_by_buyer', '_input_charset' => 'utf-8', - 'partner' => options[:pid] || Alipay.pid, - 'seller_id' => options[:pid] || Alipay.pid, + 'partner' => options[:partner] || options[:pid] || Alipay.pid, + 'seller_id' => options[:seller_id] || options[:pid] || Alipay.pid, 'payment_type' => '1' }.merge(params) @@ -26,8 +26,8 @@ def self.trade_create_by_buyer_url(params, options = {}) params = { 'service' => 'trade_create_by_buyer', '_input_charset' => 'utf-8', - 'partner' => options[:pid] || Alipay.pid, - 'seller_id' => options[:pid] || Alipay.pid, + 'partner' => options[:partner] || options[:pid] || Alipay.pid, + 'seller_id' => options[:seller_id] || options[:pid] || Alipay.pid, 'payment_type' => '1' }.merge(params) @@ -46,8 +46,8 @@ def self.create_direct_pay_by_user_url(params, options = {}) params = { 'service' => 'create_direct_pay_by_user', '_input_charset' => 'utf-8', - 'partner' => options[:pid] || Alipay.pid, - 'seller_id' => options[:pid] || Alipay.pid, + 'partner' => options[:partner] || options[:pid] || Alipay.pid, + 'seller_id' => options[:seller_id] || options[:pid] || Alipay.pid, 'payment_type' => '1' }.merge(params) @@ -62,8 +62,8 @@ def self.create_direct_pay_by_user_wap_url(params, options = {}) params = { 'service' => 'alipay.wap.create.direct.pay.by.user', '_input_charset' => 'utf-8', - 'partner' => options[:pid] || Alipay.pid, - 'seller_id' => options[:pid] || Alipay.pid, + 'partner' => options[:partner] || options[:pid] || Alipay.pid, + 'seller_id' => options[:seller_id] || options[:pid] || Alipay.pid, 'payment_type' => '1' }.merge(params) @@ -202,8 +202,8 @@ def self.create_forex_trade_wap_url(params, options = {}) params = { 'service' => 'create_forex_trade_wap', '_input_charset' => 'utf-8', - 'partner' => options[:pid] || Alipay.pid, - 'seller_id' => options[:pid] || Alipay.pid + 'partner' => options[:partner] || options[:pid] || Alipay.pid, + 'seller_id' => options[:seller_id] || options[:pid] || Alipay.pid, }.merge(params) request_uri(params, options).to_s From 9c7a4ee3797d62545f1091d9f523cdcac56d7fdf Mon Sep 17 00:00:00 2001 From: Rei Date: Sat, 16 Jun 2018 15:48:37 +0800 Subject: [PATCH 34/56] update version to 0.15.1 --- CHANGELOG.md | 4 ++++ README.md | 4 ++-- lib/alipay/version.rb | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e4a58f..dd85c9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## master +## v0.15.1 (2018-06-16) + +- Add `partner` and `seller_id` options for legacy API, thanks @KQyongzhang #83 + ## v0.15.0 (2017-08-27) - Add `Alipay::Client` for open API. diff --git a/README.md b/README.md index 6ec3b09..a5cfbae 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ To install using [Bundler](http://bundler.io/). Add this line to your application's Gemfile: ```ruby -gem 'alipay', '~> 0.15.0' +gem 'alipay', '~> 0.15.1' ``` Then run: @@ -22,7 +22,7 @@ $ bundle Or you can manually install using [RubyGems](http://rubygems.org/): ```bash -$ gem install alipay -v 0.15.0 +$ gem install alipay -v 0.15.1 ``` ## Getting Started diff --git a/lib/alipay/version.rb b/lib/alipay/version.rb index 26fff81..b1a0c41 100644 --- a/lib/alipay/version.rb +++ b/lib/alipay/version.rb @@ -1,3 +1,3 @@ module Alipay - VERSION = "0.15.0" + VERSION = "0.15.1" end From a38fc68deec2e781454d7aaaf61fc1dccb20e370 Mon Sep 17 00:00:00 2001 From: Rei Date: Sat, 16 Jun 2018 15:54:31 +0800 Subject: [PATCH 35/56] update travis ci ruby version --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 012cdab..e2b5ecf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: ruby rvm: - - 2.3.0 - - 2.2.4 - - 2.1.8 + - 2.5.1 + - 2.4.4 + - 2.3.6 From a525989b659da970e08bc8fcd1b004453bfed89f Mon Sep 17 00:00:00 2001 From: Rei Date: Thu, 22 Nov 2018 23:48:54 +0800 Subject: [PATCH 36/56] update document, fix #85 --- README.md | 4 ++-- doc/quick_start_cn.md | 48 +++++++++++++++++++++---------------------- doc/quick_start_en.md | 48 +++++++++++++++++++++---------------------- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index a5cfbae..e47ac3c 100644 --- a/README.md +++ b/README.md @@ -56,12 +56,12 @@ ALIPAY_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nTq43T5...OVUAQb3R\n-----END PUBLI ```ruby @alipay_client.page_execute_url( method: 'alipay.trade.page.pay', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'test' - }.to_json(ascii_only: true), # to_json(ascii_only: true) is important! + }, ascii_only: true), # ascii_only is important! timestamp: '2016-04-01 00:00:00' ) diff --git a/doc/quick_start_cn.md b/doc/quick_start_cn.md index b60f85e..8afa0ad 100644 --- a/doc/quick_start_cn.md +++ b/doc/quick_start_cn.md @@ -103,12 +103,12 @@ Alipay::Client.page_execute_url method: 'alipay.trade.page.pay', return_url: 'https://mystore.com/orders/20160401000000/return', notify_url: 'https://mystore.com/orders/20160401000000/notify', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'Example #123' - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' ``` @@ -141,13 +141,13 @@ Alipay::Client.page_execute_url method: 'alipay.trade.wap.pay', return_url: 'https://mystore.com/orders/20160401000000/return', notify_url: 'https://mystore.com/orders/20160401000000/notify', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '20160401000000', product_code: 'QUICK_WAP_WAY', total_amount: '0.01', subject: 'Example: 456' quit_url: 'https://mystore.com/orders/20160401000000/' - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' ``` @@ -174,11 +174,11 @@ Alipay::Client.execute response = @client.execute( method: 'alipay.trade.precreate', notify_url: 'https://mystore.com/orders/20160401000000/notify', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '20160401000000', total_amount: '50.00', subject: 'QR Code Test' - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_trade_precreate_response\":{\"code\"...' @@ -217,11 +217,11 @@ Alipay::Client.execute (扫码支付) total_amount: '0.01', subject: 'Example #654', enable_pay_channels: 'balance,pcreditpayInstallment', - extend_params: { + extend_params: JSON.generate({ hb_fq_num: '6'', hb_fq_seller_percent: '0' } - }.to_json(ascii_only: true) + }, ascii_only: true) ) ``` 情景:商城网站不提供分期选项,但允许客户在支付宝的支付过程中自行决定分期付款。 @@ -230,13 +230,13 @@ Alipay::Client.execute (扫码支付) method: 'alipay.trade.page.pay', return_url: 'https://mystore.com/orders/20160401000000/return', notify_url: 'https://mystore.com/orders/20160401000000/notify', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'Example #654', enable_pay_channels: 'balance,pcreditpayInstallment', - }.to_json(ascii_only: true) + }, ascii_only: true) ) ``` #### 值得注意的参数 @@ -299,9 +299,9 @@ Alipay::Client.execute ```ruby response = @client.execute( method: 'alipay.trade.query', - biz_content: { + biz_content: JSON.generate({ trade_no: '2013112611001004680073956707', - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_trade_query_response\":{\"code\"...' @@ -333,9 +333,9 @@ Alipay::Client.execute response = @client.execute( method: 'alipay.trade.close', notify_url: 'https://mystore.com/orders/20160401000000/notify', - biz_content: { + biz_content: JSON.generate({ trade_no: '2013112611001004680073956707', - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_trade_close_response\":{\"code\"...' @@ -365,9 +365,9 @@ Alipay::Client.execute ```ruby response = @client.execute( method: 'alipay.trade.cancel', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '20160401000000', - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_trade_cancel_response\":{\"code\"...' @@ -400,11 +400,11 @@ Alipay::Client.execute ```ruby response = @client.execute( method: 'alipay.trade.refund', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '6c50789a0610', out_request_no: '6c50789a0610-1', refund_amount: '10.12' - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_trade_refund_response\":{\"code\"...' @@ -438,10 +438,10 @@ Alipay::Client.execute ```ruby response = @client.execute( method: 'alipay.trade.fastpay.refund.query', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '6c50789a0610', out_request_no: '6c50789a0610-1' - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_trade_fastpay_refund_query_response\":{\"code\"...' @@ -473,12 +473,12 @@ Alipay::Client.execute ```ruby response = @client.execute( method: 'alipay.fund.trans.toaccount.transfer', - biz_content: { + biz_content: JSON.generate({ out_biz_no: '3142321423432', payee_type: 'ALIPAY_LOGONID', payee_account: 'customer@example.com', amount: '12.23' - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_fund_trans_toaccount_transfer_response\":{\"code\"...' @@ -511,9 +511,9 @@ Alipay::Client.execute ```ruby response = @client.execute( method: 'alipay.fund.trans.order.query', - biz_content: { + biz_content: JSON.generate({ out_biz_no: '3142321423432', - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_fund_trans_order_query_response\":{\"code\"...' diff --git a/doc/quick_start_en.md b/doc/quick_start_en.md index 17e0aaf..20ba4b6 100644 --- a/doc/quick_start_en.md +++ b/doc/quick_start_en.md @@ -101,12 +101,12 @@ This client method will generate a payment URL for redirecting customers to. method: 'alipay.trade.page.pay', return_url: 'https://mystore.com/orders/20160401000000/return', notify_url: 'https://mystore.com/orders/20160401000000/notify', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'Example #123' - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' ``` @@ -143,13 +143,13 @@ This method will generate a payment URL for redirecting customers to. method: 'alipay.trade.wap.pay', return_url: 'https://mystore.com/orders/20160401000000/return', notify_url: 'https://mystore.com/orders/20160401000000/notify', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '20160401000000', product_code: 'QUICK_WAP_WAY', total_amount: '0.01', subject: 'Example: 456' quit_url: 'https://mystore.com/orders/20160401000000/' - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => 'https://openapi.alipaydev.com/gateway.do?app_id=2016...' ``` @@ -177,11 +177,11 @@ Alipay::Client.execute response = @client.execute( method: 'alipay.trade.precreate', notify_url: 'https://mystore.com/orders/20160401000000/notify', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '20160401000000', total_amount: '50.00', subject: 'QR Code Test' - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_trade_precreate_response\":{\"code\"...' @@ -214,7 +214,7 @@ Scenario: Customer pre-select a six-installment payment plan before going throug method: 'alipay.trade.page.pay', return_url: 'https://mystore.com/orders/20160401000000/return', notify_url: 'https://mystore.com/orders/20160401000000/notify', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', @@ -224,7 +224,7 @@ Scenario: Customer pre-select a six-installment payment plan before going throug hb_fq_num: '6'', hb_fq_seller_percent: '0' } - }.to_json(ascii_only: true) + }, ascii_only: true) ) ``` Scenario: Customer select an installment plan or their choice at Alipay's payment page. @@ -233,13 +233,13 @@ Scenario: Customer select an installment plan or their choice at Alipay's paymen method: 'alipay.trade.page.pay', return_url: 'https://mystore.com/orders/20160401000000/return', notify_url: 'https://mystore.com/orders/20160401000000/notify', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '20160401000000', product_code: 'FAST_INSTANT_TRADE_PAY', total_amount: '0.01', subject: 'Example #654', enable_pay_channels: 'balance,pcreditpayInstallment', - }.to_json(ascii_only: true) + }, ascii_only: true) ) ``` #### Notebale Parameters @@ -313,9 +313,9 @@ Alipay::Client.execute ```ruby response = @client.execute( method: 'alipay.trade.query', - biz_content: { + biz_content: JSON.generate({ trade_no: '2013112611001004680073956707', - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_trade_query_response\":{\"code\"...' @@ -350,9 +350,9 @@ Alipay::Client.execute response = @client.execute( method: 'alipay.trade.close', notify_url: 'https://mystore.com/orders/20160401000000/notify', - biz_content: { + biz_content: JSON.generate({ trade_no: '2013112611001004680073956707', - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_trade_close_response\":{\"code\"...' @@ -388,9 +388,9 @@ Alipay::Client.execute ```ruby response = @client.execute( method: 'alipay.trade.cancel', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '20160401000000', - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_trade_cancel_response\":{\"code\"...' @@ -425,11 +425,11 @@ Secenario: Customer request refund on a ¥10.12 item on a ¥210.85 order(payme ```ruby response = @client.execute( method: 'alipay.trade.refund', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '6c50789a0610', out_request_no: '6c50789a0610-1', refund_amount: '10.12' - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_trade_refund_response\":{\"code\"...' @@ -468,10 +468,10 @@ Alipay::Client.execute ```ruby response = @client.execute( method: 'alipay.trade.fastpay.refund.query', - biz_content: { + biz_content: JSON.generate({ out_trade_no: '6c50789a0610', out_request_no: '6c50789a0610-1' - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_trade_fastpay_refund_query_response\":{\"code\"...' @@ -506,12 +506,12 @@ Alipay::Client.execute ```ruby response = @client.execute( method: 'alipay.fund.trans.toaccount.transfer', - biz_content: { + biz_content: JSON.generate({ out_biz_no: '3142321423432', payee_type: 'ALIPAY_LOGONID', payee_account: 'customer@example.com', amount: '12.23' - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_fund_trans_toaccount_transfer_response\":{\"code\"...' @@ -548,9 +548,9 @@ Alipay::Client.execute ```ruby response = @client.execute( method: 'alipay.fund.trans.order.query', - biz_content: { + biz_content: JSON.generate({ out_biz_no: '3142321423432', - }.to_json(ascii_only: true) + }, ascii_only: true) ) # => '{\"alipay_fund_trans_order_query_response\":{\"code\"...' From 03604e2b2fdb22e8acf52e12278601ea905ccc71 Mon Sep 17 00:00:00 2001 From: Kexian Zhong Date: Fri, 2 Aug 2019 20:44:46 +0800 Subject: [PATCH 37/56] Fix page_execute_form invalid-signature charset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 发现有如下异常: 调试错误,请回到请求来源地,重新发起请求。 错误代码 invalid-signature 错误原因: 验签出错,请确认charset参数放在了URL查询字符串中且各参数值使用charset参数指示的字符集编码 需要在表单地址中设置charset才能调起支付。 --- lib/alipay/client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index ccea6a3..e704f27 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -102,7 +102,7 @@ def page_execute_url(params) def page_execute_form(params) params = prepare_params(params) - html = %Q() + html = %Q() params.each do |key, value| html << %Q() end From 550dd7fd43a58fe92789c123d5e9874415beb0f8 Mon Sep 17 00:00:00 2001 From: Rei Date: Fri, 2 Aug 2019 21:09:49 +0800 Subject: [PATCH 38/56] Remove bundler version --- alipay.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alipay.gemspec b/alipay.gemspec index 6947be4..c01d5c3 100644 --- a/alipay.gemspec +++ b/alipay.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.add_development_dependency "bundler", "~> 1.3" + spec.add_development_dependency "bundler" spec.add_development_dependency "rake" spec.add_development_dependency "minitest" spec.add_development_dependency "webmock" From 8cb11618afa558264e0a967ac1c07623ea570d28 Mon Sep 17 00:00:00 2001 From: Rei Date: Fri, 2 Aug 2019 21:16:06 +0800 Subject: [PATCH 39/56] Fix page execute form test --- test/alipay/client_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index 2c6d977..25d17eb 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -48,7 +48,7 @@ def test_page_execute_url_for_alipay_trade_page_pay end def test_page_execute_form_for_alipay_trade_page_pay - form = "" + form = "
" assert_equal form, @client.page_execute_form( method: 'alipay.trade.page.pay', From 69b7bfe60658066136440fbe569c5e323457fb6c Mon Sep 17 00:00:00 2001 From: Rei Date: Fri, 2 Aug 2019 21:19:21 +0800 Subject: [PATCH 40/56] Update version --- CHANGELOG.md | 4 ++++ lib/alipay/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd85c9c..ed52c32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## master +## v0.15.2 (2019-08-02) + +- page_execute_form invalid-signature charset, thanks @lingceng #90 + ## v0.15.1 (2018-06-16) - Add `partner` and `seller_id` options for legacy API, thanks @KQyongzhang #83 diff --git a/lib/alipay/version.rb b/lib/alipay/version.rb index b1a0c41..0e33eb2 100644 --- a/lib/alipay/version.rb +++ b/lib/alipay/version.rb @@ -1,3 +1,3 @@ module Alipay - VERSION = "0.15.1" + VERSION = "0.15.2" end From e14032c5e14eff99f8961d98341c4ff387fce9f4 Mon Sep 17 00:00:00 2001 From: moohao <645694636@qq.com> Date: Sat, 19 Oct 2019 10:56:16 +0800 Subject: [PATCH 41/56] Support certificate signature --- doc/rsa_key_cn.md | 41 +++++++++++++++++++++++++++++ lib/alipay/client.rb | 8 ++++++ lib/alipay/utils.rb | 31 ++++++++++++++++++++++ test/alipay/client_test.rb | 10 ++++--- test/alipay/utils_test.rb | 9 +++++++ test/test_helper.rb | 54 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+), 4 deletions(-) diff --git a/doc/rsa_key_cn.md b/doc/rsa_key_cn.md index 3f04fae..9815d33 100644 --- a/doc/rsa_key_cn.md +++ b/doc/rsa_key_cn.md @@ -6,6 +6,7 @@ * [生成应用密钥](#生成应用密钥) * [验证参数](#验证参数) * [补充格式到支付宝公钥](#补充格式到支付宝公钥) +* [使用证书签名方式](#使用证书签名方式) ### 生成应用密钥 #### 在 Ruby 下生成 RSA2 密钥 @@ -62,3 +63,43 @@ pub_key.scan(/.{64}|.+$/).join("\n").insert(0, "-----BEGIN PUBLIC KEY-----\n").i # => "-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----\n" ``` +# 使用证书签名方式 + +## 应用证书配置 +按照官方文档进行新建应用配置证书签名 https://docs.open.alipay.com/291/twngcd/ + +配置完成后,可以得到 `xxx.com_私钥.txt alipayCertPublicKey_RSA2.crt appCertPublicKey_2019082600000001.crt alipayRootCert.crt` 四个文件。 + +### 应用私钥补充格式 +```ruby +app_private_key = File.read('xxx.com_私钥.txt') +app_private_key = app_private_key.scan(/.{64}|.+$/).join("\n").insert(0, "-----BEGIN RSA PRIVATE KEY-----\n").insert(-1, "\n-----END RSA PRIVATE KEY-----\n") +``` +### 处理应用阿里云公钥 +```ruby +alipay_public_key = File.read('alipayCertPublicKey_RSA2.crt') +alipay_public_key = OpenSSL::X509::Certificate.new(alipay_public_key).public_key.to_s +``` +### 得到应用公钥证书sn +```ruby +app_cert = File.read('appCertPublicKey_2019082600000001.crt') +app_cert_sn = Alipay::Utils.get_cert_sn(app_cert) +# => "28d1147972121b91734da59aa10f3c16" +``` +### 得到支付宝根证书sn +```ruby +alipay_root_cert = File.read('alipayRootCert.crt') +alipay_root_cert_sn = Alipay::Utils.get_root_cert_sn(alipay_root_cert) +# => "28d1147972121b91734da59aa10f3c16_28d1147972121b91734da59aa10f3c16" +``` +### 使用 +```ruby +@alipay_client = Alipay::Client.new( + url: API_URL, + app_id: APP_ID, + app_private_key: app_private_key, + alipay_public_key: alipay_public_key, + app_cert_sn: app_cert_sn, + alipay_root_cert_sn: alipay_root_cert_sn +) +``` \ No newline at end of file diff --git a/lib/alipay/client.rb b/lib/alipay/client.rb index e704f27..a1ffb7e 100644 --- a/lib/alipay/client.rb +++ b/lib/alipay/client.rb @@ -37,6 +37,8 @@ def initialize(options) @format = options['format'] || 'json' @charset = options['charset'] || 'UTF-8' @sign_type = options['sign_type'] || 'RSA2' + @app_cert_sn = options['app_cert_sn'] + @alipay_root_cert_sn = options['alipay_root_cert_sn'] end # Generate a query string that use for APP SDK excute. @@ -183,6 +185,12 @@ def prepare_params(params) 'version' => '1.0', 'timestamp' => Time.now.localtime('+08:00').strftime("%Y-%m-%d %H:%M:%S") }.merge(::Alipay::Utils.stringify_keys(params)) + if !@app_cert_sn.nil? && !@alipay_root_cert_sn.nil? + params = params.merge({ + 'app_cert_sn' => @app_cert_sn, + 'alipay_root_cert_sn' => @alipay_root_cert_sn + }) + end params['sign'] = sign(params) params end diff --git a/lib/alipay/utils.rb b/lib/alipay/utils.rb index f1727f3..88329a7 100644 --- a/lib/alipay/utils.rb +++ b/lib/alipay/utils.rb @@ -15,5 +15,36 @@ def self.generate_batch_no batch_no = t.strftime('%Y%m%d%H%M%S') + t.nsec.to_s batch_no.ljust(24, rand(10).to_s) end + + # get app_cert_sn + def self.get_cert_sn(str, match_algo = false) + return nil if str.nil? + certificate = OpenSSL::X509::Certificate.new(str) + if match_algo + begin + return unless certificate.public_key.is_a?(OpenSSL::PKey::RSA) + rescue => exception + return + end + end + issuer_arr = OpenSSL::X509::Name.new(certificate.issuer).to_a + issuer = issuer_arr.reverse.map { |item| item[0..1].join('=') }.join(',') + serial = OpenSSL::BN.new(certificate.serial).to_s + OpenSSL::Digest::MD5.hexdigest(issuer + serial) + end + + # get alipay_root_cert_sn + def self.get_root_cert_sn(str) + return nil if str.nil? + arr = str.scan(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/) + arr_sn = [] + arr.each do |item| + sn = get_cert_sn(item, true) + unless sn.nil? + arr_sn.push(sn) + end + end + arr_sn.join('_') + end end end diff --git a/test/alipay/client_test.rb b/test/alipay/client_test.rb index 25d17eb..ae62443 100644 --- a/test/alipay/client_test.rb +++ b/test/alipay/client_test.rb @@ -9,7 +9,9 @@ def setup format: 'json', charset: 'UTF-8', alipay_public_key: TEST_RSA_PUBLIC_KEY, - sign_type: 'RSA2' + sign_type: 'RSA2', + app_cert_sn: '28d1147972121b91734da59aa10f3c16', + alipay_root_cert_sn: '28d1147972121b91734da59aa10f3c16_28d1147972121b91734da59aa10f3c16' ) end @@ -18,7 +20,7 @@ def test_client_initialize end def test_sdk_execute_for_alipay_trade_app_pay - string = 'app_id=2016000000000000&charset=UTF-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=NbpUhRJ0HYSSIQGPxHOVLQpHcvzOyN8%2FeIhxq26Gglpx2%2BQvsJdffxg8bySiVcXryiZQbw1ZReZAO5HkokEtS8hiHp%2BNVnFPzZQLAiWXqHoO3hXkZTofjma618uhNfjFFYatgZX9Zio8XpSU9UaBL7sJiTqWY5cmDTKwbcc%2Ftck%3D' + string = 'app_id=2016000000000000&charset=UTF-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&app_cert_sn=28d1147972121b91734da59aa10f3c16&alipay_root_cert_sn=28d1147972121b91734da59aa10f3c16_28d1147972121b91734da59aa10f3c16&sign=Zr2UkxZcW69WB8bNsERgJrd73zBdiOeb08CmoI946Fiuh2dIYZKW8MqYEa46nPbqPL4Q0gv3djc1IWFMgY9J4Ku8TltV5NRxrIrtfRxVZa59Sg%2BlAI7EiHENqJfXpZptn%2BFa7SaV4ZzLsMWaNC2RceF2unPH17%2FZr1ZpkzUFYlE%3D' assert_equal string, @client.sdk_execute( method: 'alipay.trade.page.pay', @@ -33,7 +35,7 @@ def test_sdk_execute_for_alipay_trade_app_pay end def test_page_execute_url_for_alipay_trade_page_pay - url = 'https://openapi.alipaydev.com/gateway.do?app_id=2016000000000000&charset=UTF-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22FAST_INSTANT_TRADE_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&sign=CXmicQH4t0AxsFGodhA3n824fKsJHH%2BAt2RsRvr1poYhhPpk4UUxd0Zrrv9ytNP28Ov1seNl0zZf%2FPfIYmg5ZquUHyFbYrXL3zDtJQntwRYBncZlKGkNDt0Bg0fMU2bJK3xr8TLW1FiJMb1sZ1a2PQaeeEG0PV0b7cf4KDsxIwQ%3D' + url = 'https://openapi.alipaydev.com/gateway.do?app_id=2016000000000000&charset=UTF-8&sign_type=RSA2&version=1.0×tamp=2016-04-01+00%3A00%3A00&method=alipay.trade.page.pay&biz_content=%7B%22out_trade_no%22%3A%2220160401000000%22%2C%22product_code%22%3A%22FAST_INSTANT_TRADE_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%22test%22%7D&app_cert_sn=28d1147972121b91734da59aa10f3c16&alipay_root_cert_sn=28d1147972121b91734da59aa10f3c16_28d1147972121b91734da59aa10f3c16&sign=SEOXB35dlg5c4P1dwFnZDuvI%2FAsjMx6rfC9Y2F83lLx86GK1C1bQ05HR%2Fw%2FQhAbnXVIKt1%2Fh9IrDx6q8mlalQbWiNMrWHDZHm5%2FMFYln29LDNxtOT9T3s4bSvzzwfjzmnock68H7dW%2BJm%2Bbf5q6KFzen6iiR3%2Fy9BPwgldJhRh0%3D' assert_equal url, @client.page_execute_url( method: 'alipay.trade.page.pay', @@ -48,7 +50,7 @@ def test_page_execute_url_for_alipay_trade_page_pay end def test_page_execute_form_for_alipay_trade_page_pay - form = "
" + form = "
" assert_equal form, @client.page_execute_form( method: 'alipay.trade.page.pay', diff --git a/test/alipay/utils_test.rb b/test/alipay/utils_test.rb index 93c5bf1..029431b 100644 --- a/test/alipay/utils_test.rb +++ b/test/alipay/utils_test.rb @@ -9,4 +9,13 @@ def test_stringify_keys def test_generate_batch_no assert_equal(24, Alipay::Utils.generate_batch_no.size) end + + def test_get_cert_sn + assert_equal('28d1147972121b91734da59aa10f3c16', Alipay::Utils.get_cert_sn(TEST_APP_CERT)) + end + + def test_get_root_cert_sn + assert_equal('28d1147972121b91734da59aa10f3c16_28d1147972121b91734da59aa10f3c16', Alipay::Utils.get_root_cert_sn(TEST_ALIPAY_ROOT_CERT)) + end + end diff --git a/test/test_helper.rb b/test/test_helper.rb index 686e0c2..c583f3e 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -32,3 +32,57 @@ DEorJMaPK5vXiA== -----END PRIVATE KEY----- EOF + +TEST_APP_CERT = < Date: Fri, 15 May 2020 10:41:19 +0800 Subject: [PATCH 42/56] update to 0.16.0 --- CHANGELOG.md | 4 ++++ lib/alipay/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed52c32..f48ad1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## master +## v0.16.0 (2020-05-15) + +- Support certificate signature, thanks @moohao #92 + ## v0.15.2 (2019-08-02) - page_execute_form invalid-signature charset, thanks @lingceng #90 diff --git a/lib/alipay/version.rb b/lib/alipay/version.rb index 0e33eb2..3ad09d9 100644 --- a/lib/alipay/version.rb +++ b/lib/alipay/version.rb @@ -1,3 +1,3 @@ module Alipay - VERSION = "0.15.2" + VERSION = "0.16.0" end From ed08dc7f2b22d1817e56d78030128e3ed886b30a Mon Sep 17 00:00:00 2001 From: JWong Date: Mon, 31 Jul 2017 09:59:35 +0800 Subject: [PATCH 43/56] =?UTF-8?q?appends=20alipay=20service=20=E2=80=98ali?= =?UTF-8?q?pay.commerce.qrcode.create=E2=80=99.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/alipay/service.rb | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/alipay/service.rb b/lib/alipay/service.rb index 21ca205..523685d 100644 --- a/lib/alipay/service.rb +++ b/lib/alipay/service.rb @@ -209,6 +209,33 @@ def self.create_forex_trade_wap_url(params, options = {}) request_uri(params, options).to_s end + # Alipay Commerce + # alipay doc: https://global.alipay.com/service/merchant_QR_Code/15 + + CREATE_MERCHANT_QR_CODE_REQUIRED_PARAMS = %w( biz_type biz_data ) + CREATE_MERCHANT_QR_CODE_REQUIRED_BIZ_DATA_PARAMS = %w( secondary_merchant_industry secondary_merchant_id secondary_merchant_name trans_currency currency ) + def self.create_merchant_qr_code(params, options = {}) + params = Utils.stringify_keys(params) + check_required_params(params, CREATE_MERCHANT_QR_CODE_REQUIRED_PARAMS) + check_required_params(params['biz_data'], CREATE_MERCHANT_QR_CODE_REQUIRED_BIZ_DATA_PARAMS) + + data = params.delete('biz_data') + biz_data = data.map do |key, value| + "\"#{key}\": \"#{value}\"" + end.join(',') + biz_data = "{#{biz_data}}" + + params = { + 'service' => 'alipay.commerce.qrcode.create', + '_input_charset' => 'utf-8', + 'partner' => options[:pid] || Alipay.pid, + 'timestamp' => Time.now.utc.strftime('%Y-%m-%d %H:%M:%S').to_s, + 'biz_data' => biz_data + }.merge(params) + + request_uri(params, options).to_s + end + def self.request_uri(params, options = {}) uri = URI(GATEWAY_URL) uri.query = URI.encode_www_form(sign_params(params, options)) @@ -226,7 +253,7 @@ def self.check_required_params(params, names) return if !Alipay.debug_mode? names.each do |name| - warn("Alipay Warn: missing required option: #{name}") unless params.has_key?(name) + warn("Alipay Warn: missing required option: #{name}") unless params.with_indifferent_access.has_key?(name) end end From 8debab7ebfe60729ff5a7553e69f764f54d7810f Mon Sep 17 00:00:00 2001 From: JWong Date: Mon, 31 Jul 2017 10:49:57 +0800 Subject: [PATCH 44/56] refactor biz_data params error checking. --- lib/alipay/service.rb | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/alipay/service.rb b/lib/alipay/service.rb index 523685d..0121ad0 100644 --- a/lib/alipay/service.rb +++ b/lib/alipay/service.rb @@ -217,12 +217,18 @@ def self.create_forex_trade_wap_url(params, options = {}) def self.create_merchant_qr_code(params, options = {}) params = Utils.stringify_keys(params) check_required_params(params, CREATE_MERCHANT_QR_CODE_REQUIRED_PARAMS) - check_required_params(params['biz_data'], CREATE_MERCHANT_QR_CODE_REQUIRED_BIZ_DATA_PARAMS) + biz_data = nil + + if params['biz_data'].present? + params['biz_data'] = Utils.stringify_keys(params['biz_data']) + check_required_params(params['biz_data'], CREATE_MERCHANT_QR_CODE_REQUIRED_BIZ_DATA_PARAMS) + + data = params.delete('biz_data') + biz_data = data.map do |key, value| + "\"#{key}\": \"#{value}\"" + end.join(',') + end - data = params.delete('biz_data') - biz_data = data.map do |key, value| - "\"#{key}\": \"#{value}\"" - end.join(',') biz_data = "{#{biz_data}}" params = { @@ -253,7 +259,7 @@ def self.check_required_params(params, names) return if !Alipay.debug_mode? names.each do |name| - warn("Alipay Warn: missing required option: #{name}") unless params.with_indifferent_access.has_key?(name) + warn("Alipay Warn: missing required option: #{name}") unless params.has_key?(name) end end From 559152f65ba754ab39e8c3f1dcece701ec9b83c7 Mon Sep 17 00:00:00 2001 From: Jack Wong Date: Sat, 19 Aug 2017 14:12:02 +0800 Subject: [PATCH 45/56] Append acquirer overseas spot refund feature --- lib/alipay/service.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/alipay/service.rb b/lib/alipay/service.rb index 0121ad0..74b6cd0 100644 --- a/lib/alipay/service.rb +++ b/lib/alipay/service.rb @@ -242,6 +242,21 @@ def self.create_merchant_qr_code(params, options = {}) request_uri(params, options).to_s end + ACQUIRER_OVERSEAS_SPOT_REFUND_REQUIRED_PARAMS = %w( partner_trans_id partner_refund_id refund_amount currency ) + def self.acquirer_overseas_spot_refund_url(params, options= {}) + params = Utils.stringify_keys(params) + check_required_params(params, ACQUIRER_OVERSEAS_SPOT_REFUND_REQUIRED_PARAMS) + + params = { + 'service' => 'alipay.acquire.overseas.spot.refund', + '_input_charset' => 'utf-8', + 'partner' => options[:pid] || Alipay.pid, + 'sign_type' => (options[:sign_type] || Alipay.sign_type), + }.merge(params) + + request_uri(params, options).to_s + end + def self.request_uri(params, options = {}) uri = URI(GATEWAY_URL) uri.query = URI.encode_www_form(sign_params(params, options)) From 7693a4a3ce2c2c1515662f8c6c9ec153ce51fae4 Mon Sep 17 00:00:00 2001 From: Jack Wong Date: Sun, 20 Aug 2017 19:04:56 +0800 Subject: [PATCH 46/56] removes sign_type from refund service method. --- lib/alipay/service.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/alipay/service.rb b/lib/alipay/service.rb index 74b6cd0..c771b42 100644 --- a/lib/alipay/service.rb +++ b/lib/alipay/service.rb @@ -251,7 +251,6 @@ def self.acquirer_overseas_spot_refund_url(params, options= {}) 'service' => 'alipay.acquire.overseas.spot.refund', '_input_charset' => 'utf-8', 'partner' => options[:pid] || Alipay.pid, - 'sign_type' => (options[:sign_type] || Alipay.sign_type), }.merge(params) request_uri(params, options).to_s From a842bf2833cecdc66340a2d6f4a7f288ec506761 Mon Sep 17 00:00:00 2001 From: Jack Wong Date: Thu, 14 Sep 2017 14:08:50 +0800 Subject: [PATCH 47/56] append UPDATE_MERCHANT_QR_CODE --- lib/alipay/service.rb | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/alipay/service.rb b/lib/alipay/service.rb index c771b42..aa21862 100644 --- a/lib/alipay/service.rb +++ b/lib/alipay/service.rb @@ -242,6 +242,34 @@ def self.create_merchant_qr_code(params, options = {}) request_uri(params, options).to_s end + UPDATE_MERCHANT_QR_CODE_REQUIRED_PARAMS = %w( biz_type biz_data qr_code ) + def self.update_merchant_qr_code(params, options = {}) + params = Utils.stringify_keys(params) + check_required_params(params, UPDATE_MERCHANT_QR_CODE_REQUIRED_PARAMS) + biz_data = nil + + if params['biz_data'].present? + params['biz_data'] = Utils.stringify_keys(params['biz_data']) + + data = params.delete('biz_data') + biz_data = data.map do |key, value| + "\"#{key}\": \"#{value}\"" + end.join(',') + end + + biz_data = "{#{biz_data}}" + + params = { + 'service' => 'alipay.commerce.qrcode.modify', + '_input_charset' => 'utf-8', + 'partner' => options[:pid] || Alipay.pid, + 'timestamp' => Time.now.utc.strftime('%Y-%m-%d %H:%M:%S').to_s, + 'biz_data' => biz_data + }.merge(params) + + request_uri(params, options).to_s + end + ACQUIRER_OVERSEAS_SPOT_REFUND_REQUIRED_PARAMS = %w( partner_trans_id partner_refund_id refund_amount currency ) def self.acquirer_overseas_spot_refund_url(params, options= {}) params = Utils.stringify_keys(params) From c658415142d7e315693d48bd8b778da7679028a0 Mon Sep 17 00:00:00 2001 From: Jack Wong Date: Sat, 18 Nov 2017 10:15:14 +0800 Subject: [PATCH 48/56] Append Alipay Acquirer Query method. --- lib/alipay/service.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/alipay/service.rb b/lib/alipay/service.rb index aa21862..7e0e132 100644 --- a/lib/alipay/service.rb +++ b/lib/alipay/service.rb @@ -270,6 +270,20 @@ def self.update_merchant_qr_code(params, options = {}) request_uri(params, options).to_s end + ACQUIRER_OVERSEAS_QUERY_REQUIRED_PARAMS = %w(partner_trans_id) + def self.acquire_overseas_query(params, options = {}) + params = Utils.stringify_keys(params) + check_required_params(params, ACQUIRER_OVERSEAS_QUERY_REQUIRED_PARAMS) + + params = { + 'service' => 'alipay.acquire.overseas.query', + '_input_charset' => 'utf-8', + 'partner' => options[:pid] || Alipay.pid, + } + + request_uri(params, options).to_s + end + ACQUIRER_OVERSEAS_SPOT_REFUND_REQUIRED_PARAMS = %w( partner_trans_id partner_refund_id refund_amount currency ) def self.acquirer_overseas_spot_refund_url(params, options= {}) params = Utils.stringify_keys(params) From a55f3eaa54c2e6d8459d5030b4171d59b4f6bc36 Mon Sep 17 00:00:00 2001 From: Jack Wong Date: Sun, 19 Nov 2017 16:24:56 +0800 Subject: [PATCH 49/56] append missing merge params and fix typo error --- lib/alipay/service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/alipay/service.rb b/lib/alipay/service.rb index 7e0e132..85790d1 100644 --- a/lib/alipay/service.rb +++ b/lib/alipay/service.rb @@ -271,7 +271,7 @@ def self.update_merchant_qr_code(params, options = {}) end ACQUIRER_OVERSEAS_QUERY_REQUIRED_PARAMS = %w(partner_trans_id) - def self.acquire_overseas_query(params, options = {}) + def self.acquirer_overseas_query(params, options = {}) params = Utils.stringify_keys(params) check_required_params(params, ACQUIRER_OVERSEAS_QUERY_REQUIRED_PARAMS) @@ -279,7 +279,7 @@ def self.acquire_overseas_query(params, options = {}) 'service' => 'alipay.acquire.overseas.query', '_input_charset' => 'utf-8', 'partner' => options[:pid] || Alipay.pid, - } + }.merge(params) request_uri(params, options).to_s end From 8581a5c94fdf7d2e1d1d3845eef2c20e2abd360e Mon Sep 17 00:00:00 2001 From: Chee Kah Shing <78400979+kahshing96@users.noreply.github.com> Date: Wed, 26 Jul 2023 14:30:04 +0800 Subject: [PATCH 50/56] FAVE-30480 Add Unit Tests for additional integration in Alipay Gem (#3) --- doc/legacy_api.md | 196 ++++++++++++++++++++++++++++++++++++ lib/alipay/service.rb | 7 +- test/alipay/service_test.rb | 65 ++++++++++++ 3 files changed, 263 insertions(+), 5 deletions(-) diff --git a/doc/legacy_api.md b/doc/legacy_api.md index 6c2b7c4..7fc30be 100644 --- a/doc/legacy_api.md +++ b/doc/legacy_api.md @@ -539,6 +539,202 @@ notify_params = params.except(*request.path_parameters.keys) Alipay::Notify.verify?(notify_params, options = {}) ``` +### QR Code 生成二维码 + +#### Name + +```ruby +alipay.commerce.qrcode.create +``` + +#### Definition + +```ruby +Alipay::Service.create_merchant_qr_code({PARAMS}, {OPTIONS}) +``` + +#### Example + +```ruby +create_qr_code_params = { + biz_type: "OVERSEASHOPQRCODE", + biz_data: { + address: "No.278, Road YinCheng, Shanghai, China", + country_code: "CN", + currency: "USD", + secondary_merchant_id: "xxx001", + secondary_merchant_industry: "7011", + secondary_merchant_name: "xxx Store", + store_id: "0001", + store_name: "Apple store", + trans_currency: "USD" + } +} + +Alipay::Service.create_merchant_qr_code(create_qr_code_params) +# => 'https://mapi.alipay.com/gateway.do?service=alipay.commerce.qrcode.create...' +``` + +#### ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| notify_url | optional | Alipay asyn notify url. | +| biz_type | required | Business type that is defined by Alipay, this case is “OVERSEASHOPQRCODE” | +| biz_data | required | Business data. Format:JSON | + +#### BIZ_DATA ARGUMENTS (required) + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| secondary_merchant_industry | required | Business category code of the secondary merchant. | +| secondary_merchant_id | required | The unique ID assigned by the partner to identify a secondary merchant. | +| secondary_merchant_name | required | Registration legal name of the secondary merchant, shown in the Alipay Wallet and the reconciliation file to identify a secondary merchant. | +| store_id | required | The unique ID that is assigned by the partner to identify a store of a merchant. | +| store_name | required | The name of the store. | +| trans_currency | required | The pricing currency | +| currency | required | The currency to settle with the merchant. The default value is CNY. If the pricing currency is not CNY, then the settlement currency must be either CNY or the pricing currency. | +| country_code | required | The country code that consists of two letters (alpha-2 code) | +| address | required | The address of the store where the code is created. | + +This is not a complete list of arguments, please read official document: https://global.alipay.com/docs/ac/global/qrcode_create#biz_data + +### QR Code 修改二维码 + +#### Name + +```ruby +alipay.commerce.qrcode.modify +``` + +#### Definition + +```ruby +Alipay::Service.update_merchant_qr_code({PARAMS}, {OPTIONS}) +``` + +#### Example + +```ruby +update_qr_code_params = { + biz_type: "OVERSEASHOPQRCODE", + qrcode: "https://qr.alipay.com/baxxxxx", + biz_data: { + address: "No.278, Road YinCheng, Shanghai, China", + country_code: "CN", + currency: "USD", + secondary_merchant_id: "xxx001", + secondary_merchant_industry: "7011", + secondary_merchant_name: "xxx Store", + store_id: "0001", + store_name: "Apple store", + trans_currency: "USD" + } +} + +Alipay::Service.update_merchant_qr_code(update_qr_code_params) +# => 'https://mapi.alipay.com/gateway.do?service=alipay.commerce.qrcode.modify...' +``` + +#### ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| notify_url | optional | Alipay asyn notify url. | +| biz_type | required | Business type that is defined by Alipay, this case is “OVERSEASHOPQRCODE” | +| biz_data | required | Business data. Format:JSON | +| qrcode | required | The returned QR code value after the code is generated successfully. | + +#### BIZ_DATA ARGUMENTS (required) + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| secondary_merchant_industry | required | Business category code of the secondary merchant. | +| secondary_merchant_id | required | The unique ID assigned by the partner to identify a secondary merchant. | +| secondary_merchant_name | required | Registration legal name of the secondary merchant, shown in the Alipay Wallet and the reconciliation file to identify a secondary merchant. | +| store_id | required | The unique ID that is assigned by the partner to identify a store of a merchant. | +| store_name | required | The name of the store. | +| trans_currency | required | The pricing currency | +| currency | required | The currency to settle with the merchant. The default value is CNY. If the pricing currency is not CNY, then the settlement currency must be either CNY or the pricing currency. | +| country_code | required | The country code that consists of two letters (alpha-2 code) | +| address | required | The address of the store where the code is created. | + +This is not a complete list of arguments, please read official document: https://global.alipay.com/docs/ac/global/qrcode_modify#Qb0Hc + +### 境外线下交易查询接口 + +#### Name + +```ruby +alipay.acquire.overseas.query +``` + +#### Definition + +```ruby +Alipay::Service.acquirer_overseas_query({PARAMS}, {OPTIONS}) +``` + +#### Example + +```ruby +acquirer_overseas_query_params = { + partner_trans_id: "2010121000000002" +} + +Alipay::Service.acquirer_overseas_query(acquirer_overseas_query_params) +# => 'https://mapi.alipay.com/gateway.do?service=alipay.acquire.overseas.query...' +``` + +#### ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| partner_trans_id | required | The original partner transaction ID given in the payment request | +| alipay_trans_id | optional | The transaction ID assigned by Alipay for the partner's payment request, which follows a mapping relation with the partner field plus the partner_trans_id field. When both of the fields are specified, alipay_trans_id will be verified first. | + +Document: https://global.alipay.com/docs/ac/global/overseas_query + +### 境外线下单笔退款接口 + +#### Name + +```ruby +alipay.acquire.overseas.spot.refund +``` + +#### Definition + +```ruby +Alipay::Service.acquirer_overseas_spot_refund_url({PARAMS}, {OPTIONS}) +``` + +#### Example + +```ruby +acquirer_overseas_spot_refund_params = { + partner_trans_id: "2010121000000002", + partner_refund_id: "301012133000002", + currency: "USD", + refund_amount: "0.01" +} + +Alipay::Service.acquirer_overseas_spot_refund_url(acquirer_overseas_spot_refund_params) +# => 'https://mapi.alipay.com/gateway.do?service=alipay.acquire.overseas.spot.refund...' +``` + +#### ARGUMENTS + +| Key | Requirement | Description | +| --- | ----------- | ----------- | +| partner_trans_id | required | The original partner transaction ID given in the payment request | +| partner_refund_id | required | The refund order ID in the partner system. The value of partner_refund_id cannot be the same as that of partner_trans_id. The partner_refund_id field plus the partner field identifies a refund transaction. | +| currency | required | The currency of the refund amount. | +| refund_amount | required | Refund amount, which must be less than or equal to the original transaction amount or the left transaction amount if ever refunded. | +| is_sync | optional | Indicates that the refund request is processed synchronously or asynchronously with a value of Y or N. The default value is N, which means an asynchronous notification from Alipay is returned to the merchant if the merchant has set the value of the notify_url field when sending the refund request. If the value is set as Y, it means only a synchronous response is returned to the merchant. | + +This is not a complete list of arguments, please read official document: https://global.alipay.com/docs/ac/global/spot_refund#92fa0c95 + ## Mobile::Service ### 移动支付接口 diff --git a/lib/alipay/service.rb b/lib/alipay/service.rb index 85790d1..1789887 100644 --- a/lib/alipay/service.rb +++ b/lib/alipay/service.rb @@ -209,9 +209,6 @@ def self.create_forex_trade_wap_url(params, options = {}) request_uri(params, options).to_s end - # Alipay Commerce - # alipay doc: https://global.alipay.com/service/merchant_QR_Code/15 - CREATE_MERCHANT_QR_CODE_REQUIRED_PARAMS = %w( biz_type biz_data ) CREATE_MERCHANT_QR_CODE_REQUIRED_BIZ_DATA_PARAMS = %w( secondary_merchant_industry secondary_merchant_id secondary_merchant_name trans_currency currency ) def self.create_merchant_qr_code(params, options = {}) @@ -219,7 +216,7 @@ def self.create_merchant_qr_code(params, options = {}) check_required_params(params, CREATE_MERCHANT_QR_CODE_REQUIRED_PARAMS) biz_data = nil - if params['biz_data'].present? + if params['biz_data'] params['biz_data'] = Utils.stringify_keys(params['biz_data']) check_required_params(params['biz_data'], CREATE_MERCHANT_QR_CODE_REQUIRED_BIZ_DATA_PARAMS) @@ -248,7 +245,7 @@ def self.update_merchant_qr_code(params, options = {}) check_required_params(params, UPDATE_MERCHANT_QR_CODE_REQUIRED_PARAMS) biz_data = nil - if params['biz_data'].present? + if params['biz_data'] params['biz_data'] = Utils.stringify_keys(params['biz_data']) data = params.delete('biz_data') diff --git a/test/alipay/service_test.rb b/test/alipay/service_test.rb index 26e538d..4d73062 100644 --- a/test/alipay/service_test.rb +++ b/test/alipay/service_test.rb @@ -262,4 +262,69 @@ def test_batch_trans_notify_url assert_equal 'https://mapi.alipay.com/gateway.do?service=batch_trans_notify&_input_charset=utf-8&partner=1000000000000000&pay_date=20080107¬ify_url=https%3A%2F%2Fexample.com%2Forders%2F20150401000-0001%2Fnotify&account_name=%E6%AF%9B%E6%AF%9B&detail_data=0315006%5Etestture0002%40126.com%5E%E5%B8%B8%E7%82%9C%E4%B9%B0%E5%AE%B6%5E1000.00%5Ehello&batch_no=20080107001&batch_num=1&batch_fee=1000.0&email=biz_932%40alitest.com&sign_type=MD5&sign=59c611607daafd1337e96b22404bd521', Alipay::Service.batch_trans_notify_url(options) end + + def test_create_merchant_qr_code + params = { + biz_type: "OVERSEASHOPQRCODE", + biz_data: { + address: "No.278, Road YinCheng, Shanghai, China", + country_code: "CN", + currency: "USD", + secondary_merchant_id: "xxx001", + secondary_merchant_industry: "7011", + secondary_merchant_name: "xxx Store", + store_id: "0001", + store_name: "Apple store", + trans_currency: "USD" + } + } + + current_time = Time.new(2023, 12, 12, 1, 1, 1) + Time.stub :now, current_time do + assert_equal 'https://mapi.alipay.com/gateway.do?service=alipay.commerce.qrcode.create&_input_charset=utf-8&partner=1000000000000000×tamp=2023-12-11+17%3A01%3A01&biz_data=%7B%22address%22%3A+%22No.278%2C+Road+YinCheng%2C+Shanghai%2C+China%22%2C%22country_code%22%3A+%22CN%22%2C%22currency%22%3A+%22USD%22%2C%22secondary_merchant_id%22%3A+%22xxx001%22%2C%22secondary_merchant_industry%22%3A+%227011%22%2C%22secondary_merchant_name%22%3A+%22xxx+Store%22%2C%22store_id%22%3A+%220001%22%2C%22store_name%22%3A+%22Apple+store%22%2C%22trans_currency%22%3A+%22USD%22%7D&biz_type=OVERSEASHOPQRCODE&sign_type=MD5&sign=fbfcc8c9316cc209c385eefcbceb2105', Alipay::Service.create_merchant_qr_code(params) + end + end + + def test_update_merchant_qr_code + params = { + biz_type: "OVERSEASHOPQRCODE", + qr_code: "https://qr.alipay.com/baxxxxx", + biz_data: { + address: "No.278, Road YinCheng, Shanghai, China", + country_code: "CN", + currency: "USD", + secondary_merchant_id: "xxx001", + secondary_merchant_industry: "7011", + secondary_merchant_name: "xxx Store", + store_id: "0001", + store_name: "Apple store", + trans_currency: "USD" + } + } + + current_time = Time.new(2023, 12, 12, 1, 1, 1) + Time.stub :now, current_time do + assert_equal 'https://mapi.alipay.com/gateway.do?service=alipay.commerce.qrcode.modify&_input_charset=utf-8&partner=1000000000000000×tamp=2023-12-11+17%3A01%3A01&biz_data=%7B%22address%22%3A+%22No.278%2C+Road+YinCheng%2C+Shanghai%2C+China%22%2C%22country_code%22%3A+%22CN%22%2C%22currency%22%3A+%22USD%22%2C%22secondary_merchant_id%22%3A+%22xxx001%22%2C%22secondary_merchant_industry%22%3A+%227011%22%2C%22secondary_merchant_name%22%3A+%22xxx+Store%22%2C%22store_id%22%3A+%220001%22%2C%22store_name%22%3A+%22Apple+store%22%2C%22trans_currency%22%3A+%22USD%22%7D&biz_type=OVERSEASHOPQRCODE&qr_code=https%3A%2F%2Fqr.alipay.com%2Fbaxxxxx&sign_type=MD5&sign=5a72863d023163147f2909e74b741e52', Alipay::Service.update_merchant_qr_code(params) + end + end + + def test_acquirer_overseas_query + params = { + partner_trans_id: "2010121000000002" + } + + assert_equal 'https://mapi.alipay.com/gateway.do?service=alipay.acquire.overseas.query&_input_charset=utf-8&partner=1000000000000000&partner_trans_id=2010121000000002&sign_type=MD5&sign=2a7f598bbb13d02f7de819ae689f80ba', Alipay::Service.acquirer_overseas_query(params) + end + + def test_acquirer_overseas_spot_refund_url + params = { + partner_trans_id: "2010121000000002", + partner_refund_id: "301012133000002", + currency: "USD", + refund_amount: "0.01", + is_sync: "Y" + } + + assert_equal 'https://mapi.alipay.com/gateway.do?service=alipay.acquire.overseas.spot.refund&_input_charset=utf-8&partner=1000000000000000&partner_trans_id=2010121000000002&partner_refund_id=301012133000002¤cy=USD&refund_amount=0.01&is_sync=Y&sign_type=MD5&sign=397685a0c6b2d71d0d1f374ddba331a0', Alipay::Service.acquirer_overseas_spot_refund_url(params) + end end From 027bde5df0c2f7a962d874b4985bc28e161b59d6 Mon Sep 17 00:00:00 2001 From: Rei Date: Wed, 26 Jul 2023 16:26:58 +0800 Subject: [PATCH 51/56] Add ci config --- .github/workflows/ci.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..001c979 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,19 @@ +name: CI + +on: [push, pull_request] + +jobs: + test: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + ruby: ['3.0', '3.1', '3.2'] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - run: bundle exec rake From 9702431d2482a8569e942f5942ec5c40d137716c Mon Sep 17 00:00:00 2001 From: Rei Date: Wed, 26 Jul 2023 16:54:38 +0800 Subject: [PATCH 52/56] Add release github action --- .github/workflows/release.yml | 26 ++++++++++++++++++++++++++ .github/workflows/{ci.yml => test.yml} | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release.yml rename .github/workflows/{ci.yml => test.yml} (97%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4a30066 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,26 @@ +name: Release + +on: + release: + types: [published] + +jobs: + build_and_push: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.2 + bundler-cache: true + - run: bundle exec rake + - name: Publish to RubyGems + run: | + mkdir -p $HOME/.gem + touch $HOME/.gem/credentials + chmod 0600 $HOME/.gem/credentials + printf -- "---\n:rubygems_api_key: ${RUBYGEMS_API_KEY}\n" > $HOME/.gem/credentials + gem build *.gemspec + gem push *.gem + env: + RUBYGEMS_API_KEY: "${{secrets.RUBYGEMS_API_KEY}}" diff --git a/.github/workflows/ci.yml b/.github/workflows/test.yml similarity index 97% rename from .github/workflows/ci.yml rename to .github/workflows/test.yml index 001c979..90729ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: CI +name: Test on: [push, pull_request] From ec7f813446f39d535ccefbe93a5d6cac28ad41e8 Mon Sep 17 00:00:00 2001 From: kahshingchee Date: Wed, 26 Jul 2023 18:12:23 +0800 Subject: [PATCH 53/56] fix failed test using utc --- test/alipay/service_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/alipay/service_test.rb b/test/alipay/service_test.rb index 4d73062..7c42ba0 100644 --- a/test/alipay/service_test.rb +++ b/test/alipay/service_test.rb @@ -279,9 +279,9 @@ def test_create_merchant_qr_code } } - current_time = Time.new(2023, 12, 12, 1, 1, 1) + current_time = Time.utc(2023, 12, 12, 1, 1, 1) Time.stub :now, current_time do - assert_equal 'https://mapi.alipay.com/gateway.do?service=alipay.commerce.qrcode.create&_input_charset=utf-8&partner=1000000000000000×tamp=2023-12-11+17%3A01%3A01&biz_data=%7B%22address%22%3A+%22No.278%2C+Road+YinCheng%2C+Shanghai%2C+China%22%2C%22country_code%22%3A+%22CN%22%2C%22currency%22%3A+%22USD%22%2C%22secondary_merchant_id%22%3A+%22xxx001%22%2C%22secondary_merchant_industry%22%3A+%227011%22%2C%22secondary_merchant_name%22%3A+%22xxx+Store%22%2C%22store_id%22%3A+%220001%22%2C%22store_name%22%3A+%22Apple+store%22%2C%22trans_currency%22%3A+%22USD%22%7D&biz_type=OVERSEASHOPQRCODE&sign_type=MD5&sign=fbfcc8c9316cc209c385eefcbceb2105', Alipay::Service.create_merchant_qr_code(params) + assert_equal 'https://mapi.alipay.com/gateway.do?service=alipay.commerce.qrcode.create&_input_charset=utf-8&partner=1000000000000000×tamp=2023-12-12+01%3A01%3A01&biz_data=%7B%22address%22%3A+%22No.278%2C+Road+YinCheng%2C+Shanghai%2C+China%22%2C%22country_code%22%3A+%22CN%22%2C%22currency%22%3A+%22USD%22%2C%22secondary_merchant_id%22%3A+%22xxx001%22%2C%22secondary_merchant_industry%22%3A+%227011%22%2C%22secondary_merchant_name%22%3A+%22xxx+Store%22%2C%22store_id%22%3A+%220001%22%2C%22store_name%22%3A+%22Apple+store%22%2C%22trans_currency%22%3A+%22USD%22%7D&biz_type=OVERSEASHOPQRCODE&sign_type=MD5&sign=1c8881161f5895d0e7bbc8f4f5cad06c', Alipay::Service.create_merchant_qr_code(params) end end @@ -302,9 +302,9 @@ def test_update_merchant_qr_code } } - current_time = Time.new(2023, 12, 12, 1, 1, 1) + current_time = Time.utc(2023, 12, 12, 1, 1, 1) Time.stub :now, current_time do - assert_equal 'https://mapi.alipay.com/gateway.do?service=alipay.commerce.qrcode.modify&_input_charset=utf-8&partner=1000000000000000×tamp=2023-12-11+17%3A01%3A01&biz_data=%7B%22address%22%3A+%22No.278%2C+Road+YinCheng%2C+Shanghai%2C+China%22%2C%22country_code%22%3A+%22CN%22%2C%22currency%22%3A+%22USD%22%2C%22secondary_merchant_id%22%3A+%22xxx001%22%2C%22secondary_merchant_industry%22%3A+%227011%22%2C%22secondary_merchant_name%22%3A+%22xxx+Store%22%2C%22store_id%22%3A+%220001%22%2C%22store_name%22%3A+%22Apple+store%22%2C%22trans_currency%22%3A+%22USD%22%7D&biz_type=OVERSEASHOPQRCODE&qr_code=https%3A%2F%2Fqr.alipay.com%2Fbaxxxxx&sign_type=MD5&sign=5a72863d023163147f2909e74b741e52', Alipay::Service.update_merchant_qr_code(params) + assert_equal 'https://mapi.alipay.com/gateway.do?service=alipay.commerce.qrcode.modify&_input_charset=utf-8&partner=1000000000000000×tamp=2023-12-12+01%3A01%3A01&biz_data=%7B%22address%22%3A+%22No.278%2C+Road+YinCheng%2C+Shanghai%2C+China%22%2C%22country_code%22%3A+%22CN%22%2C%22currency%22%3A+%22USD%22%2C%22secondary_merchant_id%22%3A+%22xxx001%22%2C%22secondary_merchant_industry%22%3A+%227011%22%2C%22secondary_merchant_name%22%3A+%22xxx+Store%22%2C%22store_id%22%3A+%220001%22%2C%22store_name%22%3A+%22Apple+store%22%2C%22trans_currency%22%3A+%22USD%22%7D&biz_type=OVERSEASHOPQRCODE&qr_code=https%3A%2F%2Fqr.alipay.com%2Fbaxxxxx&sign_type=MD5&sign=4d43f06b3d60cfc96750876287ef66ca', Alipay::Service.update_merchant_qr_code(params) end end From f1c30b9fd3200ffe9170cd165c66e0e4b4139e79 Mon Sep 17 00:00:00 2001 From: Rei Date: Wed, 26 Jul 2023 19:12:59 +0800 Subject: [PATCH 54/56] update version to 0.17.0 --- CHANGELOG.md | 2 ++ README.md | 4 ++-- doc/legacy_api.md | 2 +- lib/alipay/version.rb | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f48ad1d..fde1fc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## master +Newer release changelog, see https://github.com/chloerei/alipay/releases + ## v0.16.0 (2020-05-15) - Support certificate signature, thanks @moohao #92 diff --git a/README.md b/README.md index e47ac3c..c8a6544 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ To install using [Bundler](http://bundler.io/). Add this line to your application's Gemfile: ```ruby -gem 'alipay', '~> 0.15.1' +gem 'alipay' ``` Then run: @@ -22,7 +22,7 @@ $ bundle Or you can manually install using [RubyGems](http://rubygems.org/): ```bash -$ gem install alipay -v 0.15.1 +$ gem install alipay ``` ## Getting Started diff --git a/doc/legacy_api.md b/doc/legacy_api.md index 7fc30be..2444d42 100644 --- a/doc/legacy_api.md +++ b/doc/legacy_api.md @@ -11,7 +11,7 @@ Alipay official document: https://b.alipay.com/order/techService.htm . Add this line to your application's Gemfile: ```ruby -gem 'alipay', '~> 0.14.0' +gem 'alipay' ``` And then execute: diff --git a/lib/alipay/version.rb b/lib/alipay/version.rb index 3ad09d9..b146423 100644 --- a/lib/alipay/version.rb +++ b/lib/alipay/version.rb @@ -1,3 +1,3 @@ module Alipay - VERSION = "0.16.0" + VERSION = "0.17.0" end From e44145561fe3faf73953963afee22a6a6635b0ac Mon Sep 17 00:00:00 2001 From: zhenghui97 Date: Mon, 20 May 2024 10:03:41 +0800 Subject: [PATCH 55/56] Remove old code --- lib/alipay/service.rb | 89 ------------------------------------------- 1 file changed, 89 deletions(-) diff --git a/lib/alipay/service.rb b/lib/alipay/service.rb index aa82329..1789887 100644 --- a/lib/alipay/service.rb +++ b/lib/alipay/service.rb @@ -295,95 +295,6 @@ def self.acquirer_overseas_spot_refund_url(params, options= {}) request_uri(params, options).to_s end - # Alipay Commerce - # alipay doc: https://global.alipay.com/service/merchant_QR_Code/15 - - CREATE_MERCHANT_QR_CODE_REQUIRED_PARAMS = %w( biz_type biz_data ) - CREATE_MERCHANT_QR_CODE_REQUIRED_BIZ_DATA_PARAMS = %w( secondary_merchant_industry secondary_merchant_id secondary_merchant_name trans_currency currency ) - def self.create_merchant_qr_code(params, options = {}) - params = Utils.stringify_keys(params) - check_required_params(params, CREATE_MERCHANT_QR_CODE_REQUIRED_PARAMS) - biz_data = nil - - if params['biz_data'].present? - params['biz_data'] = Utils.stringify_keys(params['biz_data']) - check_required_params(params['biz_data'], CREATE_MERCHANT_QR_CODE_REQUIRED_BIZ_DATA_PARAMS) - - data = params.delete('biz_data') - biz_data = data.map do |key, value| - "\"#{key}\": \"#{value}\"" - end.join(',') - end - - biz_data = "{#{biz_data}}" - - params = { - 'service' => 'alipay.commerce.qrcode.create', - '_input_charset' => 'utf-8', - 'partner' => options[:pid] || Alipay.pid, - 'timestamp' => Time.now.utc.strftime('%Y-%m-%d %H:%M:%S').to_s, - 'biz_data' => biz_data - }.merge(params) - - request_uri(params, options).to_s - end - - UPDATE_MERCHANT_QR_CODE_REQUIRED_PARAMS = %w( biz_type biz_data qr_code ) - def self.update_merchant_qr_code(params, options = {}) - params = Utils.stringify_keys(params) - check_required_params(params, UPDATE_MERCHANT_QR_CODE_REQUIRED_PARAMS) - biz_data = nil - - if params['biz_data'].present? - params['biz_data'] = Utils.stringify_keys(params['biz_data']) - - data = params.delete('biz_data') - biz_data = data.map do |key, value| - "\"#{key}\": \"#{value}\"" - end.join(',') - end - - biz_data = "{#{biz_data}}" - - params = { - 'service' => 'alipay.commerce.qrcode.modify', - '_input_charset' => 'utf-8', - 'partner' => options[:pid] || Alipay.pid, - 'timestamp' => Time.now.utc.strftime('%Y-%m-%d %H:%M:%S').to_s, - 'biz_data' => biz_data - }.merge(params) - - request_uri(params, options).to_s - end - - ACQUIRER_OVERSEAS_QUERY_REQUIRED_PARAMS = %w(partner_trans_id) - def self.acquirer_overseas_query(params, options = {}) - params = Utils.stringify_keys(params) - check_required_params(params, ACQUIRER_OVERSEAS_QUERY_REQUIRED_PARAMS) - - params = { - 'service' => 'alipay.acquire.overseas.query', - '_input_charset' => 'utf-8', - 'partner' => options[:pid] || Alipay.pid, - }.merge(params) - - request_uri(params, options).to_s - end - - ACQUIRER_OVERSEAS_SPOT_REFUND_REQUIRED_PARAMS = %w( partner_trans_id partner_refund_id refund_amount currency ) - def self.acquirer_overseas_spot_refund_url(params, options= {}) - params = Utils.stringify_keys(params) - check_required_params(params, ACQUIRER_OVERSEAS_SPOT_REFUND_REQUIRED_PARAMS) - - params = { - 'service' => 'alipay.acquire.overseas.spot.refund', - '_input_charset' => 'utf-8', - 'partner' => options[:pid] || Alipay.pid, - }.merge(params) - - request_uri(params, options).to_s - end - def self.request_uri(params, options = {}) uri = URI(GATEWAY_URL) uri.query = URI.encode_www_form(sign_params(params, options)) From d15850e20ca2c427fc7eb00813d773453fa5c5a0 Mon Sep 17 00:00:00 2001 From: zhenghui97 Date: Mon, 20 May 2024 18:32:40 +0800 Subject: [PATCH 56/56] Replace Alipay+ domain name --- doc/legacy_api.md | 17 +++++++++-------- lib/alipay.rb | 2 +- lib/alipay/notify.rb | 2 +- lib/alipay/service.rb | 4 +--- test/alipay/notify_test.rb | 6 +++--- test/alipay/service_test.rb | 34 +++++++++++++++++----------------- test/alipay/wap/notify_test.rb | 6 +++--- test/test_helper.rb | 3 +++ 8 files changed, 38 insertions(+), 36 deletions(-) diff --git a/doc/legacy_api.md b/doc/legacy_api.md index 2444d42..f48aaa1 100644 --- a/doc/legacy_api.md +++ b/doc/legacy_api.md @@ -25,6 +25,7 @@ $ bundle ```ruby Alipay.pid = 'YOUR_PID' Alipay.key = 'YOUR_KEY' +Alipay.legacy_gateway_url = 'ALIPAY_GATEWAY_URL_FOR_YOUR_REGION' #Alipay.sign_type = 'MD5' # Available values: MD5, RSA. Default is MD5 #Alipay.debug_mode = true # Enable parameter check. Default is true. @@ -72,7 +73,7 @@ Alipay::Service.create_partner_trade_by_buyer_url( return_url: 'https://example.com/orders/20150401000-0001', notify_url: 'https://example.com/orders/20150401000-0001/notify' ) -# => 'https://mapi.alipay.com/gateway.do?service=create_partner_trade_by_buyer&...' +# => 'https://api-sea-global.alipayplus.com/gateway.do?service=create_partner_trade_by_buyer&...' ``` Guide consumer to this address to complete payment @@ -277,7 +278,7 @@ Alipay::Service.refund_fastpay_by_platform_pwd_url( }], notify_url: 'https://example.com/orders/20150401000-0001/refund_notify' ) -# => https://mapi.alipay.com/gateway.do?service=refund_fastpay_by_platform_pwd&... +# => https://api-sea-global.alipayplus.com/gateway.do?service=refund_fastpay_by_platform_pwd&... ``` #### Arguments @@ -389,7 +390,7 @@ Alipay::Service.create_forex_trade_url( total_fee: '10.00', notify_url: 'https://example.com/orders/20150401000-0001/notify' ) -# => 'https://mapi.alipay.com/gateway.do?service=create_forex_trade...' +# => 'https://api-sea-global.alipayplus.com/gateway.do?service=create_forex_trade...' ``` #### ARGUMENTS @@ -498,7 +499,7 @@ Alipay::Service.batch_trans_notify_url( batch_fee: 1000.00, email: 'biz_932@alitest.com' ) -#=> 'https://mapi.alipay.com/gateway.do?service=batch_trans_notify&...' +#=> 'https://api-sea-global.alipayplus.com/gateway.do?service=batch_trans_notify&...' ``` #### Arguments @@ -572,7 +573,7 @@ create_qr_code_params = { } Alipay::Service.create_merchant_qr_code(create_qr_code_params) -# => 'https://mapi.alipay.com/gateway.do?service=alipay.commerce.qrcode.create...' +# => 'https://api-sea-global.alipayplus.com/gateway.do?service=alipay.commerce.qrcode.create...' ``` #### ARGUMENTS @@ -633,7 +634,7 @@ update_qr_code_params = { } Alipay::Service.update_merchant_qr_code(update_qr_code_params) -# => 'https://mapi.alipay.com/gateway.do?service=alipay.commerce.qrcode.modify...' +# => 'https://api-sea-global.alipayplus.com/gateway.do?service=alipay.commerce.qrcode.modify...' ``` #### ARGUMENTS @@ -683,7 +684,7 @@ acquirer_overseas_query_params = { } Alipay::Service.acquirer_overseas_query(acquirer_overseas_query_params) -# => 'https://mapi.alipay.com/gateway.do?service=alipay.acquire.overseas.query...' +# => 'https://api-sea-global.alipayplus.com/gateway.do?service=alipay.acquire.overseas.query...' ``` #### ARGUMENTS @@ -720,7 +721,7 @@ acquirer_overseas_spot_refund_params = { } Alipay::Service.acquirer_overseas_spot_refund_url(acquirer_overseas_spot_refund_params) -# => 'https://mapi.alipay.com/gateway.do?service=alipay.acquire.overseas.spot.refund...' +# => 'https://api-sea-global.alipayplus.com/gateway.do?service=alipay.acquire.overseas.spot.refund...' ``` #### ARGUMENTS diff --git a/lib/alipay.rb b/lib/alipay.rb index 5eebf08..d20b104 100644 --- a/lib/alipay.rb +++ b/lib/alipay.rb @@ -21,7 +21,7 @@ module Alipay @sign_type = 'MD5' class << self - attr_accessor :pid, :key, :sign_type, :debug_mode + attr_accessor :pid, :key, :legacy_gateway_url, :sign_type, :debug_mode def debug_mode? !!@debug_mode diff --git a/lib/alipay/notify.rb b/lib/alipay/notify.rb index 9e54261..ea96b95 100644 --- a/lib/alipay/notify.rb +++ b/lib/alipay/notify.rb @@ -7,7 +7,7 @@ def self.verify?(params, options = {}) end def self.verify_notify_id?(pid, notify_id) - uri = URI("https://mapi.alipay.com/gateway.do") + uri = URI("#{Alipay.legacy_gateway_url}/gateway.do") uri.query = URI.encode_www_form( 'service' => 'notify_verify', 'partner' => pid, diff --git a/lib/alipay/service.rb b/lib/alipay/service.rb index 1789887..5646db3 100644 --- a/lib/alipay/service.rb +++ b/lib/alipay/service.rb @@ -1,7 +1,5 @@ module Alipay module Service - GATEWAY_URL = 'https://mapi.alipay.com/gateway.do' - CREATE_PARTNER_TRADE_BY_BUYER_REQUIRED_PARAMS = %w( out_trade_no subject logistics_type logistics_fee logistics_payment price quantity ) def self.create_partner_trade_by_buyer_url(params, options = {}) params = Utils.stringify_keys(params) @@ -296,7 +294,7 @@ def self.acquirer_overseas_spot_refund_url(params, options= {}) end def self.request_uri(params, options = {}) - uri = URI(GATEWAY_URL) + uri = URI("#{Alipay.legacy_gateway_url}/gateway.do") uri.query = URI.encode_www_form(sign_params(params, options)) uri end diff --git a/test/alipay/notify_test.rb b/test/alipay/notify_test.rb index 4599052..30e1def 100644 --- a/test/alipay/notify_test.rb +++ b/test/alipay/notify_test.rb @@ -14,21 +14,21 @@ def setup def test_unsign_notify stub_request( - :get, "https://mapi.alipay.com/gateway.do?service=notify_verify&partner=#{Alipay.pid}¬ify_id=1234" + :get, "#{Alipay.legacy_gateway_url}/gateway.do?service=notify_verify&partner=#{Alipay.pid}¬ify_id=1234" ).to_return(body: "true") assert !Alipay::Notify.verify?(@unsign_params) end def test_verify_notify_when_true stub_request( - :get, "https://mapi.alipay.com/gateway.do?service=notify_verify&partner=#{Alipay.pid}¬ify_id=1234" + :get, "#{Alipay.legacy_gateway_url}/gateway.do?service=notify_verify&partner=#{Alipay.pid}¬ify_id=1234" ).to_return(body: "true") assert Alipay::Notify.verify?(@sign_params) end def test_verify_notify_when_false stub_request( - :get, "https://mapi.alipay.com/gateway.do?service=notify_verify&partner=#{Alipay.pid}¬ify_id=1234" + :get, "#{Alipay.legacy_gateway_url}/gateway.do?service=notify_verify&partner=#{Alipay.pid}¬ify_id=1234" ).to_return(body: "false") assert !Alipay::Notify.verify?(@sign_params) end diff --git a/test/alipay/service_test.rb b/test/alipay/service_test.rb index 7c42ba0..122469e 100644 --- a/test/alipay/service_test.rb +++ b/test/alipay/service_test.rb @@ -12,7 +12,7 @@ def test_generate_create_partner_trade_by_buyer_url quantity: 1 } - assert_equal 'https://mapi.alipay.com/gateway.do?service=create_partner_trade_by_buyer&_input_charset=utf-8&partner=1000000000000000&seller_id=1000000000000000&payment_type=1&out_trade_no=1&subject=test&logistics_type=POST&logistics_fee=0&logistics_payment=SELLER_PAY&price=0.01&quantity=1&sign_type=MD5&sign=b5d30863b44acd8514a49b0320fb2aa2', Alipay::Service.create_partner_trade_by_buyer_url(options) + assert_equal "#{Alipay.legacy_gateway_url}/gateway.do?service=create_partner_trade_by_buyer&_input_charset=utf-8&partner=1000000000000000&seller_id=1000000000000000&payment_type=1&out_trade_no=1&subject=test&logistics_type=POST&logistics_fee=0&logistics_payment=SELLER_PAY&price=0.01&quantity=1&sign_type=MD5&sign=b5d30863b44acd8514a49b0320fb2aa2", Alipay::Service.create_partner_trade_by_buyer_url(options) end def test_generate_trade_create_by_buyer_url @@ -25,7 +25,7 @@ def test_generate_trade_create_by_buyer_url price: '0.01', quantity: 1 } - assert_equal 'https://mapi.alipay.com/gateway.do?service=trade_create_by_buyer&_input_charset=utf-8&partner=1000000000000000&seller_id=1000000000000000&payment_type=1&out_trade_no=1&subject=test&logistics_type=POST&logistics_fee=0&logistics_payment=SELLER_PAY&price=0.01&quantity=1&sign_type=MD5&sign=2d296368fea70a127da939558c970bab', Alipay::Service.trade_create_by_buyer_url(options) + assert_equal "#{Alipay.legacy_gateway_url}/gateway.do?service=trade_create_by_buyer&_input_charset=utf-8&partner=1000000000000000&seller_id=1000000000000000&payment_type=1&out_trade_no=1&subject=test&logistics_type=POST&logistics_fee=0&logistics_payment=SELLER_PAY&price=0.01&quantity=1&sign_type=MD5&sign=2d296368fea70a127da939558c970bab", Alipay::Service.trade_create_by_buyer_url(options) end def test_generate_create_direct_pay_by_user_url @@ -35,7 +35,7 @@ def test_generate_create_direct_pay_by_user_url price: '0.01', quantity: 1 } - assert_equal 'https://mapi.alipay.com/gateway.do?service=create_direct_pay_by_user&_input_charset=utf-8&partner=1000000000000000&seller_id=1000000000000000&payment_type=1&out_trade_no=1&subject=test&price=0.01&quantity=1&sign_type=MD5&sign=682ad02280fca7d4c0fd22678fdddeef', Alipay::Service.create_direct_pay_by_user_url(options) + assert_equal "#{Alipay.legacy_gateway_url}/gateway.do?service=create_direct_pay_by_user&_input_charset=utf-8&partner=1000000000000000&seller_id=1000000000000000&payment_type=1&out_trade_no=1&subject=test&price=0.01&quantity=1&sign_type=MD5&sign=682ad02280fca7d4c0fd22678fdddeef", Alipay::Service.create_direct_pay_by_user_url(options) end def test_generate_create_direct_pay_by_user_wap_url @@ -44,7 +44,7 @@ def test_generate_create_direct_pay_by_user_wap_url subject: 'test', total_fee: '0.01' } - assert_equal 'https://mapi.alipay.com/gateway.do?service=alipay.wap.create.direct.pay.by.user&_input_charset=utf-8&partner=1000000000000000&seller_id=1000000000000000&payment_type=1&out_trade_no=1&subject=test&total_fee=0.01&sign_type=MD5&sign=6530de6e3cba153cd4ca7edc48b91f96', Alipay::Service.create_direct_pay_by_user_wap_url(options) + assert_equal "#{Alipay.legacy_gateway_url}/gateway.do?service=alipay.wap.create.direct.pay.by.user&_input_charset=utf-8&partner=1000000000000000&seller_id=1000000000000000&payment_type=1&out_trade_no=1&subject=test&total_fee=0.01&sign_type=MD5&sign=6530de6e3cba153cd4ca7edc48b91f96", Alipay::Service.create_direct_pay_by_user_wap_url(options) end def test_refund_fastpay_by_platform_pwd_url @@ -59,7 +59,7 @@ def test_refund_fastpay_by_platform_pwd_url notify_url: '/some_url', refund_date: '2015-01-01 00:00:00' } - assert_equal 'https://mapi.alipay.com/gateway.do?service=refund_fastpay_by_platform_pwd&_input_charset=utf-8&partner=1000000000000000&seller_user_id=1000000000000000&refund_date=2015-01-01+00%3A00%3A00&batch_num=1&detail_data=1%5E0.01%5Etest&batch_no=123456789¬ify_url=%2Fsome_url&sign_type=MD5&sign=def57a58e1ac21f70c45e41bd3697368', Alipay::Service.refund_fastpay_by_platform_pwd_url(options) + assert_equal "#{Alipay.legacy_gateway_url}/gateway.do?service=refund_fastpay_by_platform_pwd&_input_charset=utf-8&partner=1000000000000000&seller_user_id=1000000000000000&refund_date=2015-01-01+00%3A00%3A00&batch_num=1&detail_data=1%5E0.01%5Etest&batch_no=123456789¬ify_url=%2Fsome_url&sign_type=MD5&sign=def57a58e1ac21f70c45e41bd3697368", Alipay::Service.refund_fastpay_by_platform_pwd_url(options) end def test_forex_refund_url @@ -71,7 +71,7 @@ def test_forex_refund_url reason: 'reason', gmt_return: '20150101000000' } - assert_equal 'https://mapi.alipay.com/gateway.do?service=forex_refund&partner=1000000000000000&_input_charset=utf-8&gmt_return=20150101000000&out_return_no=1&out_trade_no=12345678980&return_amount=0.01¤cy=USD&reason=reason&sign_type=MD5&sign=c9681fff5505fe993d1b2b8141308d0d', Alipay::Service.forex_refund_url(options) + assert_equal "#{Alipay.legacy_gateway_url}/gateway.do?service=forex_refund&partner=1000000000000000&_input_charset=utf-8&gmt_return=20150101000000&out_return_no=1&out_trade_no=12345678980&return_amount=0.01¤cy=USD&reason=reason&sign_type=MD5&sign=c9681fff5505fe993d1b2b8141308d0d", Alipay::Service.forex_refund_url(options) end def test_generate_create_forex_trade_url @@ -82,7 +82,7 @@ def test_generate_create_forex_trade_url currency: 'EUR', total_fee: '0.01', } - assert_equal 'https://mapi.alipay.com/gateway.do?service=create_forex_trade&_input_charset=utf-8&partner=1000000000000000¬ify_url=https%3A%2F%2Fexample.com%2Fnotify&subject=test&out_trade_no=1¤cy=EUR&total_fee=0.01&sign_type=MD5&sign=f24fd4d76acabf860263a40805138380', Alipay::Service.create_forex_trade_url(options) + assert_equal "#{Alipay.legacy_gateway_url}/gateway.do?service=create_forex_trade&_input_charset=utf-8&partner=1000000000000000¬ify_url=https%3A%2F%2Fexample.com%2Fnotify&subject=test&out_trade_no=1¤cy=EUR&total_fee=0.01&sign_type=MD5&sign=f24fd4d76acabf860263a40805138380", Alipay::Service.create_forex_trade_url(options) end def test_close_trade @@ -94,7 +94,7 @@ def test_close_trade EOF stub_request( :get, - %r|https://mapi\.alipay\.com/gateway\.do.*| + STUB_URL_REGEX ).to_return(body: response_body) assert_equal response_body, Alipay::Service.close_trade( @@ -145,7 +145,7 @@ def test_single_trade_query EOF stub_request( :get, - %r|https://mapi\.alipay\.com/gateway\.do.*| + STUB_URL_REGEX ).to_return(body: response_body) assert_equal response_body, Alipay::Service.single_trade_query( @@ -162,7 +162,7 @@ def test_should_send_goods_confirm_by_platform EOF stub_request( :get, - %r|https://mapi\.alipay\.com/gateway\.do.*| + STUB_URL_REGEX ).to_return(body: body) assert_equal body, Alipay::Service.send_goods_confirm_by_platform( @@ -226,7 +226,7 @@ def test_account_page_query
EOF stub_request( - :get, %r|https://mapi\.alipay\.com/gateway\.do.*| + :get, STUB_URL_REGEX ).to_return(:body => body) assert_equal body, Alipay::Service.account_page_query( page_no: 1, @@ -245,7 +245,7 @@ def test_create_forex_trade_wap_url return_url: 'https://example.com/orders/20150401000-0001', notify_url: 'https://example.com/orders/20150401000-0001/notify' } - assert_equal 'https://mapi.alipay.com/gateway.do?service=create_forex_trade_wap&_input_charset=utf-8&partner=1000000000000000&seller_id=1000000000000000&out_trade_no=20150401000-0001&subject=Order+Name&merchant_url=http%3A%2F%2Fexample.com%2Fitemback&total_fee=10.00¤cy=USD&return_url=https%3A%2F%2Fexample.com%2Forders%2F20150401000-0001¬ify_url=https%3A%2F%2Fexample.com%2Forders%2F20150401000-0001%2Fnotify&sign_type=MD5&sign=f15d9e3d885c12f1a994048342c07bef', Alipay::Service.create_forex_trade_wap_url(options) + assert_equal "#{Alipay.legacy_gateway_url}/gateway.do?service=create_forex_trade_wap&_input_charset=utf-8&partner=1000000000000000&seller_id=1000000000000000&out_trade_no=20150401000-0001&subject=Order+Name&merchant_url=http%3A%2F%2Fexample.com%2Fitemback&total_fee=10.00¤cy=USD&return_url=https%3A%2F%2Fexample.com%2Forders%2F20150401000-0001¬ify_url=https%3A%2F%2Fexample.com%2Forders%2F20150401000-0001%2Fnotify&sign_type=MD5&sign=f15d9e3d885c12f1a994048342c07bef", Alipay::Service.create_forex_trade_wap_url(options) end def test_batch_trans_notify_url @@ -260,7 +260,7 @@ def test_batch_trans_notify_url pay_date: '20080107' } - assert_equal 'https://mapi.alipay.com/gateway.do?service=batch_trans_notify&_input_charset=utf-8&partner=1000000000000000&pay_date=20080107¬ify_url=https%3A%2F%2Fexample.com%2Forders%2F20150401000-0001%2Fnotify&account_name=%E6%AF%9B%E6%AF%9B&detail_data=0315006%5Etestture0002%40126.com%5E%E5%B8%B8%E7%82%9C%E4%B9%B0%E5%AE%B6%5E1000.00%5Ehello&batch_no=20080107001&batch_num=1&batch_fee=1000.0&email=biz_932%40alitest.com&sign_type=MD5&sign=59c611607daafd1337e96b22404bd521', Alipay::Service.batch_trans_notify_url(options) + assert_equal "#{Alipay.legacy_gateway_url}/gateway.do?service=batch_trans_notify&_input_charset=utf-8&partner=1000000000000000&pay_date=20080107¬ify_url=https%3A%2F%2Fexample.com%2Forders%2F20150401000-0001%2Fnotify&account_name=%E6%AF%9B%E6%AF%9B&detail_data=0315006%5Etestture0002%40126.com%5E%E5%B8%B8%E7%82%9C%E4%B9%B0%E5%AE%B6%5E1000.00%5Ehello&batch_no=20080107001&batch_num=1&batch_fee=1000.0&email=biz_932%40alitest.com&sign_type=MD5&sign=59c611607daafd1337e96b22404bd521", Alipay::Service.batch_trans_notify_url(options) end def test_create_merchant_qr_code @@ -281,7 +281,7 @@ def test_create_merchant_qr_code current_time = Time.utc(2023, 12, 12, 1, 1, 1) Time.stub :now, current_time do - assert_equal 'https://mapi.alipay.com/gateway.do?service=alipay.commerce.qrcode.create&_input_charset=utf-8&partner=1000000000000000×tamp=2023-12-12+01%3A01%3A01&biz_data=%7B%22address%22%3A+%22No.278%2C+Road+YinCheng%2C+Shanghai%2C+China%22%2C%22country_code%22%3A+%22CN%22%2C%22currency%22%3A+%22USD%22%2C%22secondary_merchant_id%22%3A+%22xxx001%22%2C%22secondary_merchant_industry%22%3A+%227011%22%2C%22secondary_merchant_name%22%3A+%22xxx+Store%22%2C%22store_id%22%3A+%220001%22%2C%22store_name%22%3A+%22Apple+store%22%2C%22trans_currency%22%3A+%22USD%22%7D&biz_type=OVERSEASHOPQRCODE&sign_type=MD5&sign=1c8881161f5895d0e7bbc8f4f5cad06c', Alipay::Service.create_merchant_qr_code(params) + assert_equal "#{Alipay.legacy_gateway_url}/gateway.do?service=alipay.commerce.qrcode.create&_input_charset=utf-8&partner=1000000000000000×tamp=2023-12-12+01%3A01%3A01&biz_data=%7B%22address%22%3A+%22No.278%2C+Road+YinCheng%2C+Shanghai%2C+China%22%2C%22country_code%22%3A+%22CN%22%2C%22currency%22%3A+%22USD%22%2C%22secondary_merchant_id%22%3A+%22xxx001%22%2C%22secondary_merchant_industry%22%3A+%227011%22%2C%22secondary_merchant_name%22%3A+%22xxx+Store%22%2C%22store_id%22%3A+%220001%22%2C%22store_name%22%3A+%22Apple+store%22%2C%22trans_currency%22%3A+%22USD%22%7D&biz_type=OVERSEASHOPQRCODE&sign_type=MD5&sign=1c8881161f5895d0e7bbc8f4f5cad06c", Alipay::Service.create_merchant_qr_code(params) end end @@ -304,7 +304,7 @@ def test_update_merchant_qr_code current_time = Time.utc(2023, 12, 12, 1, 1, 1) Time.stub :now, current_time do - assert_equal 'https://mapi.alipay.com/gateway.do?service=alipay.commerce.qrcode.modify&_input_charset=utf-8&partner=1000000000000000×tamp=2023-12-12+01%3A01%3A01&biz_data=%7B%22address%22%3A+%22No.278%2C+Road+YinCheng%2C+Shanghai%2C+China%22%2C%22country_code%22%3A+%22CN%22%2C%22currency%22%3A+%22USD%22%2C%22secondary_merchant_id%22%3A+%22xxx001%22%2C%22secondary_merchant_industry%22%3A+%227011%22%2C%22secondary_merchant_name%22%3A+%22xxx+Store%22%2C%22store_id%22%3A+%220001%22%2C%22store_name%22%3A+%22Apple+store%22%2C%22trans_currency%22%3A+%22USD%22%7D&biz_type=OVERSEASHOPQRCODE&qr_code=https%3A%2F%2Fqr.alipay.com%2Fbaxxxxx&sign_type=MD5&sign=4d43f06b3d60cfc96750876287ef66ca', Alipay::Service.update_merchant_qr_code(params) + assert_equal "#{Alipay.legacy_gateway_url}/gateway.do?service=alipay.commerce.qrcode.modify&_input_charset=utf-8&partner=1000000000000000×tamp=2023-12-12+01%3A01%3A01&biz_data=%7B%22address%22%3A+%22No.278%2C+Road+YinCheng%2C+Shanghai%2C+China%22%2C%22country_code%22%3A+%22CN%22%2C%22currency%22%3A+%22USD%22%2C%22secondary_merchant_id%22%3A+%22xxx001%22%2C%22secondary_merchant_industry%22%3A+%227011%22%2C%22secondary_merchant_name%22%3A+%22xxx+Store%22%2C%22store_id%22%3A+%220001%22%2C%22store_name%22%3A+%22Apple+store%22%2C%22trans_currency%22%3A+%22USD%22%7D&biz_type=OVERSEASHOPQRCODE&qr_code=https%3A%2F%2Fqr.alipay.com%2Fbaxxxxx&sign_type=MD5&sign=4d43f06b3d60cfc96750876287ef66ca", Alipay::Service.update_merchant_qr_code(params) end end @@ -313,7 +313,7 @@ def test_acquirer_overseas_query partner_trans_id: "2010121000000002" } - assert_equal 'https://mapi.alipay.com/gateway.do?service=alipay.acquire.overseas.query&_input_charset=utf-8&partner=1000000000000000&partner_trans_id=2010121000000002&sign_type=MD5&sign=2a7f598bbb13d02f7de819ae689f80ba', Alipay::Service.acquirer_overseas_query(params) + assert_equal "#{Alipay.legacy_gateway_url}/gateway.do?service=alipay.acquire.overseas.query&_input_charset=utf-8&partner=1000000000000000&partner_trans_id=2010121000000002&sign_type=MD5&sign=2a7f598bbb13d02f7de819ae689f80ba", Alipay::Service.acquirer_overseas_query(params) end def test_acquirer_overseas_spot_refund_url @@ -325,6 +325,6 @@ def test_acquirer_overseas_spot_refund_url is_sync: "Y" } - assert_equal 'https://mapi.alipay.com/gateway.do?service=alipay.acquire.overseas.spot.refund&_input_charset=utf-8&partner=1000000000000000&partner_trans_id=2010121000000002&partner_refund_id=301012133000002¤cy=USD&refund_amount=0.01&is_sync=Y&sign_type=MD5&sign=397685a0c6b2d71d0d1f374ddba331a0', Alipay::Service.acquirer_overseas_spot_refund_url(params) + assert_equal "#{Alipay.legacy_gateway_url}/gateway.do?service=alipay.acquire.overseas.spot.refund&_input_charset=utf-8&partner=1000000000000000&partner_trans_id=2010121000000002&partner_refund_id=301012133000002¤cy=USD&refund_amount=0.01&is_sync=Y&sign_type=MD5&sign=397685a0c6b2d71d0d1f374ddba331a0", Alipay::Service.acquirer_overseas_spot_refund_url(params) end end diff --git a/test/alipay/wap/notify_test.rb b/test/alipay/wap/notify_test.rb index 74b4f54..c573261 100644 --- a/test/alipay/wap/notify_test.rb +++ b/test/alipay/wap/notify_test.rb @@ -17,21 +17,21 @@ def setup def test_unsign_notify stub_request( - :get, "https://mapi.alipay.com/gateway.do?service=notify_verify&partner=#{Alipay.pid}¬ify_id=#{@notify_id}" + :get, "#{Alipay.legacy_gateway_url}/gateway.do?service=notify_verify&partner=#{Alipay.pid}¬ify_id=#{@notify_id}" ).to_return(body: "true") assert !Alipay::Wap::Notify.verify?(@notify_params) end def test_verify_notify_when_true stub_request( - :get, "https://mapi.alipay.com/gateway.do?service=notify_verify&partner=#{Alipay.pid}¬ify_id=#{@notify_id}" + :get, "#{Alipay.legacy_gateway_url}/gateway.do?service=notify_verify&partner=#{Alipay.pid}¬ify_id=#{@notify_id}" ).to_return(body: "true") assert Alipay::Wap::Notify.verify?(@sign_params) end def test_verify_notify_when_false stub_request( - :get, "https://mapi.alipay.com/gateway.do?service=notify_verify&partner=#{Alipay.pid}¬ify_id=#{@notify_id}" + :get, "#{Alipay.legacy_gateway_url}/gateway.do?service=notify_verify&partner=#{Alipay.pid}¬ify_id=#{@notify_id}" ).to_return(body: "false") assert !Alipay::Wap::Notify.verify?(@sign_params) end diff --git a/test/test_helper.rb b/test/test_helper.rb index c583f3e..1e0c4d9 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -4,6 +4,9 @@ Alipay.pid = '1000000000000000' Alipay.key = '10000000000000000000000000000000' +Alipay.legacy_gateway_url = 'https://api-sea-global.alipayplus.com' + +STUB_URL_REGEX = %r{https://api-sea-global\.alipayplus\.com/gateway\.do.*}.freeze TEST_RSA_PUBLIC_KEY = <