fuiou 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 39f9fe07a187ad5b56257dec8cd148cb12df1d46bf60bd00a223103bd83fe25b
4
+ data.tar.gz: d52fc1de0c697d3482604e172d39b7655fff65e52407b9f1d68f7db9327f4e47
5
+ SHA512:
6
+ metadata.gz: f9c6cb4e695237134c5f599704036960bda0463b24b7d0c4356b3c8b9c630323b8e561d9774aa3e94b8d8a555fbfee4558025049cdf6fddeda52d99d8bab7947
7
+ data.tar.gz: 458b731db237cb0e01ba47203e6e40e3becebc0694a24dd3e996f2dc984ada98c24e7b6a85ab1b6af16fe6a82dc200cadc70baec9205e99b891cff5c83d6917c
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rdoc/task'
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = 'rdoc'
13
+ rdoc.title = 'Tenpay'
14
+ rdoc.options << '--line-numbers'
15
+ rdoc.rdoc_files.include('README.rdoc')
16
+ rdoc.rdoc_files.include('lib/**/*.rb')
17
+ end
18
+
19
+ Bundler::GemHelper.install_tasks
20
+
21
+ require 'rake/testtask'
22
+
23
+ Rake::TestTask.new(:test) do |t|
24
+ t.libs << 'lib'
25
+ t.libs << 'test'
26
+ t.pattern = 'test/**/*_test.rb'
27
+ t.verbose = false
28
+ end
29
+
30
+ task default: :test
data/lib/fuiou.rb ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fuiou/service'
4
+ require 'fuiou/sign'
5
+ require 'fuiou/version'
6
+ require 'openssl'
7
+
8
+ module Fuiou
9
+ class << self
10
+ # mch_id 商户号
11
+ # ins_id 机构号
12
+ # ins_private_key 机构号私钥
13
+ # ins_public_key 机构号公钥
14
+ # notify_url 回调通知地址
15
+ attr_accessor :mch_id, :notify_url
16
+ attr_accessor :ins_id, :ins_private_key, :ins_public_key
17
+ end
18
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rest_client'
4
+ require 'cgi'
5
+ require 'json'
6
+ require 'active_support/core_ext/hash/conversions'
7
+
8
+ module Fuiou
9
+ module Service
10
+ FUIOU_21_URL = "https://fundwx.fuiou.com/preCreate".freeze
11
+ FUIOU_30_URL = "https://fundwx.fuiou.com/commonQuery".freeze
12
+ ENCODE_TYPE_GBK = "GBK"
13
+
14
+ class << self
15
+ INVOKE_PRE_CREATE_REQUIRED_FIELDS = %i[version ins_cd mchnt_cd term_id random_str order_type goods_des mchnt_order_no order_amt term_ip txn_begin_ts notify_url goods_detail]
16
+ # 主扫统一下单: https://fundwx.fuiou.com/doc/#/scanpay/api?id=_32-%e4%b8%bb%e6%89%ab%e7%bb%9f%e4%b8%80%e4%b8%8b%e5%8d%95
17
+ def invoke_pre_create(params)
18
+ check_required_options(params, INVOKE_PRE_CREATE_REQUIRED_FIELDS)
19
+ payload = xmlify_payload(params)
20
+ invoke_remote(FUIOU_21_URL, { req: payload })
21
+ end
22
+
23
+ INVOKE_COMMON_QUERY_REQUIRED_FIELDS = %i[version ins_cd mchnt_cd term_id order_type mchnt_order_no random_str]
24
+ # 订单查询: https://fundwx.fuiou.com/doc/#/scanpay/api?id=_34-%e8%ae%a2%e5%8d%95%e6%9f%a5%e8%af%a2
25
+ def invoke_common_query(params)
26
+ check_required_options(params, INVOKE_COMMON_QUERY_REQUIRED_FIELDS)
27
+ payload = xmlify_payload(params)
28
+ invoke_remote(FUIOU_30_URL, { req: payload })
29
+ end
30
+
31
+ def invoke_remote(url, payload, options = {})
32
+ resp = RestClient.post(
33
+ url,
34
+ payload,
35
+ {
36
+ content_type: "application/x-www-form-urlencoded",
37
+ charset: ENCODE_TYPE_GBK
38
+ }
39
+ )
40
+ Hash.from_xml(CGI.unescape(resp.body))["xml"]
41
+ end
42
+
43
+ private
44
+
45
+ def xmlify_payload(params)
46
+ rsa_string = params.sort.map{|k,v| "#{k}=#{v}&" unless k.start_with?("sign") || k.start_with?("reserved")}.join
47
+ rsa_string = rsa_string.delete_suffix("&") if rsa_string.end_with?("&")
48
+
49
+ sign = Fuiou::Sign.rsa_sign(rsa_string)
50
+
51
+ xml = "<?xml version=\"1.0\" encoding=\"GBK\" standalone=\"yes\"?><xml>#{params.except(:sign).sort.map { |k, v| "<#{k}>#{v}</#{k}>" }.join}<sign>#{sign}</sign></xml>"
52
+ (xml.force_encoding(ENCODE_TYPE_GBK)).force_encoding(ENCODE_TYPE_GBK)
53
+ end
54
+
55
+ def check_required_options(options, names)
56
+ names.each do |name|
57
+ warn("Fuiou Warn: missing required option: #{name}") unless options.has_key?(name)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
data/lib/fuiou/sign.rb ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+ require 'base64'
5
+ require 'cgi'
6
+
7
+ module Fuiou
8
+ module Sign
9
+ SIGN_TYPE_MD5 = "MD5"
10
+ ENCODE_TYPE_GBK = "GBK"
11
+
12
+ class << self
13
+ def rsa_sign(rsa_string)
14
+ rsa = OpenSSL::PKey::RSA.new(Fuiou.ins_private_key)
15
+ sign = rsa.sign(SIGN_TYPE_MD5, rsa_string.encode(ENCODE_TYPE_GBK))
16
+ Base64.strict_encode64(sign)
17
+ CGI.escape(Base64.strict_encode64(sign))
18
+ end
19
+
20
+ def rsa_verify?(rsa_string, sign)
21
+ rsa = OpenSSL::PKey::RSA.new(Fuiou.ins_public_key)
22
+ rsa.verify(
23
+ SIGN_TYPE_MD5,
24
+ Base64.strict_decode64(CGI.unescape(sign)),
25
+ rsa_string.encode(ENCODE_TYPE_GBK)
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fuiou
4
+ VERSION = "0.1.0".freeze
5
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class ServiceTest < MiniTest::Test
6
+ def test_invoke_pre_create
7
+ params = {
8
+ curr_type: "",
9
+ mchnt_cd: "0002900F0370542",
10
+ goods_des: "djkfkas",
11
+ reserved_fy_term_type: "",
12
+ reserved_hb_fq_seller_percent: "",
13
+ mchnt_order_no: "#{Time.now.strftime("%Y%m%d%H%M%S")}#{rand(100000)}",
14
+ goods_tag: "",
15
+ reserved_expire_minute: "",
16
+ random_str: SecureRandom.hex,
17
+ order_type: "ALIPAY",
18
+ ins_cd: "08A9999999",
19
+ txn_begin_ts: Time.now.strftime("%Y%m%d%H%M%S"),
20
+ reserved_fy_term_id: "",
21
+ reserved_fy_term_sn: "",
22
+ term_id: "12345678",
23
+ reserved_limit_pay: "",
24
+ notify_url: "http://www.wrx.cn",
25
+ reserved_hb_fq_num: "",
26
+ version: "1",
27
+ goods_detail: "asasda",
28
+ reserved_device_info: "",
29
+ addn_inf: "144333,95025",
30
+ order_amt: "1",
31
+ reserved_sub_appid: "",
32
+ term_ip: "127.0.0.1"
33
+ }
34
+
35
+ response_body = "%3C%3Fxml+version%3D%221.0%22+encoding%3D%22GBK%22+standalone%3D%22yes%22%3F%3E%3Cxml%3E%3Cins_cd%3E08A9999999%3C%2Fins_cd%3E%3Cmchnt_cd%3E0002900F0370542%3C%2Fmchnt_cd%3E%3Corder_type%3EALIPAY%3C%2Forder_type%3E%3Cqr_code%3Ehttps%3A%2F%2Fqr.alipay.com%2Fbax06313qu87v9btinki008f%3C%2Fqr_code%3E%3Crandom_str%3ETG0APYN6F58PFZDVL6A0QCK7KPO2V81G%3C%2Frandom_str%3E%3Creserved_addn_inf%3E144333%2C95025%3C%2Freserved_addn_inf%3E%3Creserved_channel_order_id%3E%3C%2Freserved_channel_order_id%3E%3Creserved_fy_order_no%3E%3C%2Freserved_fy_order_no%3E%3Creserved_fy_trace_no%3E070305690151%3C%2Freserved_fy_trace_no%3E%3Cresult_code%3E000000%3C%2Fresult_code%3E%3Cresult_msg%3ESUCCESS%3C%2Fresult_msg%3E%3Csession_id%3E%3C%2Fsession_id%3E%3Csign%3Ed40CAB86Tw5HGOb%2FoVhsagF%2FL3S4STxksXFHWgzWsUmgMTfDyfA9GCtRAWjuhAkHXOtETJbc6k8u%2FPDbNFA9FGsSdlblgT4rlFPZx3JymSjKrVap5UG0AvV9Cqkd%2FikyeM0cXFR%2FCGuTLdFPoHcQZARiEJxdyzJw5DT8CgaQ9aU%3D%3C%2Fsign%3E%3Cterm_id%3E%3C%2Fterm_id%3E%3C%2Fxml%3E"
36
+
37
+ stub_request(:post, "https://fundwx.fuiou.com/preCreate").to_return(body: response_body)
38
+
39
+ resp = Fuiou::Service.invoke_pre_create(params)
40
+
41
+ assert_equal resp["result_code"], "000000"
42
+ assert_equal resp["result_msg"], "SUCCESS"
43
+ end
44
+
45
+ def test_invoke_common_query
46
+ params = {
47
+ version: "1",
48
+ ins_cd: "08A9999999",
49
+ mchnt_cd: "0002900F0370542",
50
+ term_id: "12345678",
51
+ order_type: "ALIPAY",
52
+ mchnt_order_no: "2021071114412625524",
53
+ random_str: "a40b25ddbe20cb5a598209baea31632a",
54
+ }
55
+
56
+ response_body = "%3C%3Fxml+version%3D%221.0%22+encoding%3D%22GBK%22+standalone%3D%22yes%22%3F%3E%3Cxml%3E%3Caddn_inf%3E144333%2C95025%3C%2Faddn_inf%3E%3Cbuyer_id%3E%3C%2Fbuyer_id%3E%3Cins_cd%3E08A9999999%3C%2Fins_cd%3E%3Cmchnt_cd%3E0002900F0370542%3C%2Fmchnt_cd%3E%3Cmchnt_order_no%3E2021071114412625524%3C%2Fmchnt_order_no%3E%3Corder_amt%3E1%3C%2Forder_amt%3E%3Corder_type%3EALIPAY%3C%2Forder_type%3E%3Crandom_str%3E07DM9GMV5D1ILN4DS2IO3XO7TLOQMJOJ%3C%2Frandom_str%3E%3Creserved_buyer_logon_id%3E%3C%2Freserved_buyer_logon_id%3E%3Creserved_channel_order_id%3E%3C%2Freserved_channel_order_id%3E%3Creserved_coupon_fee%3E%3C%2Freserved_coupon_fee%3E%3Creserved_fund_bill_list%3E%3C%2Freserved_fund_bill_list%3E%3Creserved_fy_settle_dt%3E20210711%3C%2Freserved_fy_settle_dt%3E%3Creserved_fy_trace_no%3E070305690154%3C%2Freserved_fy_trace_no%3E%3Creserved_is_credit%3E%3C%2Freserved_is_credit%3E%3Creserved_txn_fin_ts%3E%3C%2Freserved_txn_fin_ts%3E%3Cresult_code%3E000000%3C%2Fresult_code%3E%3Cresult_msg%3ESUCCESS%3C%2Fresult_msg%3E%3Csign%3EPoJfrxcCg8UqoBy6Mx69oVekY4UOFh4KyG%2Fsfo2d%2BbRBWVxkMiS7hg4J%2Ftl7Qc%2FMYwImlobTkGdZYKqDv4BysGR7E7REsRwyWjsaVaPb%2BAIQMQAe2APTc3wdwiokqMU%2BQDRKHMOZiL7WeOpxkuQFRMpX1IMTYC0jpapcAbRyEJE%3D%3C%2Fsign%3E%3Cterm_id%3E%3C%2Fterm_id%3E%3Ctrans_stat%3ENOTPAY%3C%2Ftrans_stat%3E%3Ctransaction_id%3E%3C%2Ftransaction_id%3E%3C%2Fxml%3E"
57
+
58
+ stub_request(:post, "https://fundwx.fuiou.com/commonQuery").to_return(body: response_body)
59
+
60
+ resp = Fuiou::Service.invoke_common_query(params)
61
+
62
+ assert_equal resp["result_code"], "000000"
63
+ assert_equal resp["result_msg"], "SUCCESS"
64
+ end
65
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class Fuiou::SignTest < MiniTest::Test
6
+ def setup
7
+ @rsa_string = "addn_inf=144333,95025&curr_type=&goods_des=djkfkas&goods_detail=asasda&goods_tag=&ins_cd=08A9999999&mchnt_cd=0002900F0370542&mchnt_order_no=2021071113590828415&notify_url=http://www.wrx.cn&order_amt=1&order_type=ALIPAY&random_str=3af88fdf0f6702bc6b242a4222bd4d68&term_id=12345678&term_ip=127.0.0.1&txn_begin_ts=20210711135908&version=1"
8
+
9
+ @rsa_sign = "XErbDF8QAVPyGDoYIKi0loXD51XRwcWmCl7d9yt2ymM08hqWbfRnXSYqQpiMyIzo8sG%2Fu8NUn1BiLM%2FRDFUv5F2CC0w6T4NmWfEY7lMwO4UQHL%2FF73NLMHQcabr788%2FzIcV9WgNlEpAgB3IbMltnzMxfCm3Ph5%2B8dyYQaRyDYis%3D"
10
+ end
11
+
12
+ def test_rsa_sign
13
+ assert_equal @rsa_sign, Fuiou::Sign.rsa_sign(@rsa_string)
14
+ end
15
+
16
+ def test_rsa_verify
17
+ assert_equal true, Fuiou::Sign.rsa_verify?(@rsa_string, @rsa_sign)
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/conversions"
4
+ require "minitest/autorun"
5
+ require "fuiou"
6
+ require "webmock/minitest"
7
+
8
+ Fuiou.mch_id = "0002900F0370542"
9
+ Fuiou.notify_url = "http://www.wrx.cn"
10
+ Fuiou.ins_id = "08A9999999"
11
+
12
+ Fuiou.ins_private_key = "-----BEGIN RSA PRIVATE KEY-----\nMIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJgAzD8fEvBHQTyxUEeK963mjziMWG7nxpi+pDMdtWiakc6xVhhbaipLaHo4wVI92A2wr3ptGQ1/YsASEHm3m2wGOpT2vrb2Ln/S7lz1ShjTKaT8U6rKgCdpQNHUuLhBQlpJer2mcYEzG/nGzcyalOCgXC/6CySiJCWJmPyR45bJAgMBAAECgYBHFfBvAKBBwIEQ2jeaDbKBIFcQcgoVa81jt5xgz178WXUg/awu3emLeBKXPh2i0YtN87hM/+J8fnt3KbuMwMItCsTD72XFXLM4FgzJ4555CUCXBf5/tcKpS2xT8qV8QDr8oLKA18sQxWp8BMPrNp0epmwun/gwgxoyQrJUB5YgZQJBAOiVXHiTnc3KwvIkdOEPmlfePFnkD4zzcv2UwTlHWgCyM/L8SCAFclXmSiJfKSZZS7o0kIeJJ6xe3Mf4/HSlhdMCQQCnTow+TnlEhDTPtWa+TUgzOys83Q/VLikqKmDzkWJ7I12+WX6AbxxEHLD+THn0JGrlvzTEIZyCe0sjQy4LzQNzAkEAr2SjfVJkuGJlrNENSwPHMugmvusbRwH3/38ET7udBdVdE6poga1Z0al+0njMwVypnNwy+eLWhkhrWmpLh3OjfQJAI3BV8JS6xzKh5SVtn/3Kv19XJ0tEIUnn2lCjvLQdAixZnQpj61ydxie1rggRBQ/5vLSlvq3H8zOelNeUF1fT1QJADNo+tkHVXLY9H2kdWFoYTvuLexHAgrsnHxONOlSA5hcVLd1B3p9utOt3QeDf6x2i1lqhTH2w8gzjvsnx13tWqg==\n-----END RSA PRIVATE KEY-----\n"
13
+
14
+ Fuiou.ins_public_key = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYAMw/HxLwR0E8sVBHivet5o84jFhu58aYvqQzHbVompHOsVYYW2oqS2h6OMFSPdgNsK96bRkNf2LAEhB5t5tsBjqU9r629i5/0u5c9UoY0ymk/FOqyoAnaUDR1Li4QUJaSXq9pnGBMxv5xs3MmpTgoFwv+gskoiQliZj8keOWyQIDAQAB\n-----END PUBLIC KEY-----\n"
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fuiou
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - wen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-07-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '3.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '3.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 12.3.3
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 12.3.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: webmock
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5'
83
+ description: An unofficial simple fuiou pay gem
84
+ email:
85
+ - wen.sprint@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - Rakefile
91
+ - lib/fuiou.rb
92
+ - lib/fuiou/service.rb
93
+ - lib/fuiou/sign.rb
94
+ - lib/fuiou/version.rb
95
+ - test/fuiou/service_test.rb
96
+ - test/fuiou/sign_test.rb
97
+ - test/test_helper.rb
98
+ homepage:
99
+ licenses:
100
+ - MIT
101
+ metadata: {}
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubygems_version: 3.2.3
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: An unofficial simple fuiou pay gem
121
+ test_files:
122
+ - test/fuiou/service_test.rb
123
+ - test/fuiou/sign_test.rb
124
+ - test/test_helper.rb