wechat-api 0.2.1 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 197097c39777d37a55379fcd924dbfd58af2fb184502a775508b7249e2587b0a
4
- data.tar.gz: '081365b0f92bd3fe4d949b80720763466916184bb4d6937692bf3e8373da5aa6'
3
+ metadata.gz: 7acc9381cefbe26a5dd3073b59b37a3ea04ea0ef78b67051cc43c58d9649542f
4
+ data.tar.gz: 924d53913a5d0cbfc4f2d0e0cdaab722e25c9f2f8261cf07fa054ba28afba97f
5
5
  SHA512:
6
- metadata.gz: da4a09f2c50ade7695c3e9b0ba106744f48c2330aed02bfde03187327e25a31093a8fc660daeb424b230372a7e4deb46f9f1f8b2d2d3eea4a25a462939d56ca4
7
- data.tar.gz: 590da45cc6017a13c342fbf1252b69258327adeae15acfe25139fe2ffb93c1c84df175e3d708597fdbda86ef8d9b818431adabfee346328e10cc4169d7be81c7
6
+ metadata.gz: bddcb1e25208e6be2900b576b064d5aa2661a6d354c79f8943c0f1e14b85a5f4577cf4aaa4d91d14381b539cfa785c0358206c559b9a9a0aa92ee59dd81955cd
7
+ data.tar.gz: 1f3710634744e5ef735f1ed73ce5f23d02a75bfa02943dee964fe412fc70d23e52e6f21c82b550d6d065aeb8db7f2849319ad1e445877d7a43b82322c15c2b7e
data/Gemfile CHANGED
@@ -1,9 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
  # Specify your gem's dependencies in ucpaas.gemspec
3
3
  gemspec
4
-
5
- group :development do
6
- gem 'guard'
7
- gem 'guard-rspec'
8
- gem 'rspec-its'
9
- end
@@ -1,3 +1,2 @@
1
1
  require 'wechat/api'
2
- require 'wechat/pay'
3
2
  require 'wechat/qy'
@@ -4,5 +4,18 @@ require 'wechat/api/client'
4
4
  module Wechat
5
5
  #
6
6
  module Api
7
+ class Error < StandardError; end
8
+
9
+ def self.client(appid = 'origin_id')
10
+ var = "@_client_#{appid}"
11
+ if instance_variable_defined?(var)
12
+ instance_variable_get(var)
13
+ elsif block_given?
14
+ c = yield(Client)
15
+ instance_variable_set var, c
16
+ else
17
+ raise Error, :not_initialized
18
+ end
19
+ end
7
20
  end
8
21
  end
@@ -2,6 +2,7 @@ require 'multi_json'
2
2
  require 'wechat/api/message'
3
3
  require 'wechat/api/user'
4
4
  require 'wechat/api/util'
5
+ require 'wechat/api/js_ticket'
5
6
  require 'faraday'
6
7
  require 'logger'
7
8
 
@@ -10,6 +11,7 @@ module Wechat
10
11
  #
11
12
  class ResponseError < StandardError; end
12
13
  class AccessTokenExpiredError < ResponseError; end
14
+ class SubscribeError < ResponseError; end
13
15
  #
14
16
  class Client
15
17
  include Message
@@ -18,12 +20,13 @@ module Wechat
18
20
 
19
21
  API_BASE = 'https://api.weixin.qq.com/cgi-bin/'
20
22
 
21
- attr_reader :app_id, :secret
22
- attr_accessor :logger
23
+ attr_reader :app_id, :secret, :ticket
24
+ attr_accessor :logger, :site
23
25
 
24
26
  def initialize(app_id, secret)
25
27
  @app_id, @secret = app_id, secret
26
28
  @logger = Logger.new(STDOUT)
29
+ @ticket = JsTicket.new self
27
30
  @token_file = File.join('/tmp', "wechat-api-#{app_id}")
28
31
  end
29
32
 
@@ -37,10 +40,11 @@ module Wechat
37
40
  end
38
41
 
39
42
  def refresh
40
- url = format('%stoken', API_BASE)
43
+ url = format('%stoken', base_url)
41
44
  resp = connection.get(url, token_params)
42
45
  response = MultiJson.load(resp.body)
43
46
  return handle_error(response) if response['errcode']
47
+
44
48
  @access_token = response['access_token']
45
49
  File.open(@token_file, 'w') { |f| f.write(resp.body) } if @access_token
46
50
  @access_token
@@ -72,9 +76,11 @@ module Wechat
72
76
  end
73
77
 
74
78
  def with_access_token(uri, params, tried = 2)
75
- url = format('%s%s', API_BASE, uri)
79
+ url = format('%s%s', base_url, uri)
76
80
  begin
77
81
  resp = yield(url, params.merge(access_token: access_token))
82
+ raise ResponseError, resp unless resp.success?
83
+
78
84
  response = MultiJson.load(resp.body)
79
85
  handle_error(response)
80
86
  rescue AccessTokenExpiredError => e
@@ -86,6 +92,10 @@ module Wechat
86
92
 
87
93
  private
88
94
 
95
+ def base_url
96
+ site || API_BASE
97
+ end
98
+
89
99
  def debug_request
90
100
  response = yield
91
101
  logger.debug { response }
@@ -97,9 +107,11 @@ module Wechat
97
107
  when 0, nil
98
108
  response
99
109
  when 40_001, 42_001, 40_014
100
- fail AccessTokenExpiredError, response
110
+ raise AccessTokenExpiredError, response
111
+ when 43_004
112
+ raise SubscribeError, response
101
113
  else
102
- fail ResponseError, response
114
+ raise ResponseError, response
103
115
  end
104
116
  end
105
117
 
@@ -0,0 +1,22 @@
1
+ module Wechat
2
+ module Api
3
+
4
+ class JsTicket
5
+ attr_reader :client, :ticket, :expires_at
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def refresh
11
+ js = client.js_ticket
12
+ @ticket = js['ticket']
13
+ @expires_at = DateTime.now + Rational(js['expires_in'].to_i, 3600 * 24)
14
+ self
15
+ end
16
+
17
+ def expired?
18
+ expires_at.nil? || DateTime.now > expires_at
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,6 @@
1
+ require 'securerandom'
2
+ require 'digest/sha1'
3
+
1
4
  module Wechat
2
5
  module Api
3
6
  #
@@ -29,8 +32,24 @@ module Wechat
29
32
  end
30
33
 
31
34
  def js_ticket
32
- res = get 'ticket/getticket', type: :jsapi
33
- res['ticket']
35
+ get 'ticket/getticket', type: :jsapi
36
+ end
37
+
38
+ def sign(params)
39
+ str = params.to_a.sort.map { |p| p.join('=') }.join('&')
40
+ logger.debug { "to_sign: #{str}" }
41
+ Digest::SHA1.hexdigest str
42
+ end
43
+
44
+ def js_config(url: nil)
45
+ r = ticket.tap { |j| j.refresh if j.expired? }.ticket
46
+ nonce = SecureRandom.hex
47
+ timestamp = Time.now.to_i
48
+ signature = sign(noncestr: nonce, timestamp: timestamp, jsapi_ticket: r, url: url)
49
+ {
50
+ appId: app_id, timestamp: timestamp, nonceStr: nonce,
51
+ signature: signature
52
+ }.tap { |hash| logger.debug { "js_config: #{hash}, url: #{url}" } }
34
53
  end
35
54
  end
36
55
  end
@@ -2,6 +2,6 @@
2
2
  module Wechat
3
3
  #
4
4
  module Api
5
- VERSION = '0.2.1'.freeze
5
+ VERSION = '0.4.3'.freeze
6
6
  end
7
7
  end
@@ -0,0 +1,18 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ RSpec.describe Wechat::Api::JsTicket do
4
+
5
+ let(:client) { double(:client) }
6
+ subject do
7
+ Wechat::Api::JsTicket.new client
8
+ end
9
+
10
+ it :refresh do
11
+ allow(client).to receive(:js_ticket).and_return('expires_in'=> 7200, 'ticket'=> 'bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA')
12
+ subject.refresh
13
+ expect(subject.ticket).not_to be_nil
14
+ should_not be_expired
15
+ expect(subject.expires_at).to be_a(Date)
16
+ end
17
+
18
+ end
@@ -0,0 +1,19 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ RSpec.describe Wechat::Api::Util do
4
+ subject { Wechat::Api::Client.new('APPID', 'APPSECRET') }
5
+
6
+ before do
7
+ subject.logger.level = Logger::DEBUG
8
+ end
9
+
10
+ it :js_config do
11
+ allow(SecureRandom).to receive(:hex).and_return('Wm3WZYTPz0wzccnW')
12
+ allow_any_instance_of(Time).to receive(:to_i).and_return(1414587457)
13
+ allow(subject).to receive(:js_ticket).and_return('expires_in' => 7200, 'ticket' => 'sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg')
14
+ js = subject.js_config(url: 'http://mp.weixin.qq.com?params=value')
15
+ expect(js).not_to be_nil
16
+ expect(js).to include(signature: '0f9de62fce790f9a083d5c99e95740ceb90c27ed')
17
+ expect(js).to have_key :appId
18
+ end
19
+ end
@@ -13,18 +13,19 @@ Gem::Specification.new do |spec|
13
13
  spec.license = "MIT"
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0")
16
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
16
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
17
  spec.require_paths = ["lib"]
19
18
 
20
- spec.add_runtime_dependency "faraday", ">= 0.9"
21
- spec.add_runtime_dependency "rest-client", ">= 1.7.0"
22
- spec.add_runtime_dependency "multi_json", ">= 1.2"
23
- spec.add_runtime_dependency 'gyoku', '>= 1.0.0'
24
- spec.add_runtime_dependency 'nori'
25
- spec.add_development_dependency "bundler"
26
- spec.add_development_dependency "rake"
27
- spec.add_development_dependency "pry"
28
- spec.add_development_dependency "rspec", "~> 3.0"
29
- spec.add_development_dependency "webmock", "~> 1.18"
19
+ spec.add_runtime_dependency 'faraday', '~> 1.0'
20
+ spec.add_runtime_dependency 'multi_json', '~> 1.0'
21
+ spec.add_runtime_dependency 'oj', '~>3.0'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.0'
24
+ spec.add_development_dependency 'guard', '~> 2.0'
25
+ spec.add_development_dependency 'guard-rspec', '~> 4.0'
26
+ spec.add_development_dependency 'rake', '~> 13.0'
27
+ spec.add_development_dependency 'rspec', '~> 3.0'
28
+ spec.add_development_dependency 'rspec-its', '~> 1.0'
29
+ spec.add_development_dependency 'webmock', '~> 3.0'
30
+
30
31
  end
metadata CHANGED
@@ -1,163 +1,162 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wechat-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Wong
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-16 00:00:00.000000000 Z
11
+ date: 2021-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.9'
19
+ version: '1.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.9'
26
+ version: '1.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rest-client
28
+ name: multi_json
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.7.0
33
+ version: '1.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.7.0
40
+ version: '1.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: multi_json
42
+ name: oj
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.2'
47
+ version: '3.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1.2'
54
+ version: '3.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: gyoku
56
+ name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 1.0.0
62
- type: :runtime
61
+ version: '1.0'
62
+ type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 1.0.0
68
+ version: '1.0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: nori
70
+ name: guard
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :runtime
75
+ version: '2.0'
76
+ type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: '2.0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: bundler
84
+ name: guard-rspec
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: '4.0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: '4.0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rake
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: '13.0'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: '13.0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: pry
112
+ name: rspec
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - ">="
115
+ - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '0'
117
+ version: '3.0'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - ">="
122
+ - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: '0'
124
+ version: '3.0'
125
125
  - !ruby/object:Gem::Dependency
126
- name: rspec
126
+ name: rspec-its
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: '3.0'
131
+ version: '1.0'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: '3.0'
138
+ version: '1.0'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: webmock
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: '1.18'
145
+ version: '3.0'
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: '1.18'
153
- description:
152
+ version: '3.0'
153
+ description:
154
154
  email:
155
155
  - lazing@gmail.com
156
156
  executables: []
157
157
  extensions: []
158
158
  extra_rdoc_files: []
159
159
  files:
160
- - ".github/workflows/rake_spec.yml"
161
160
  - ".gitignore"
162
161
  - ".rspec"
163
162
  - Gemfile
@@ -167,25 +166,24 @@ files:
167
166
  - lib/wechat-api.rb
168
167
  - lib/wechat/api.rb
169
168
  - lib/wechat/api/client.rb
169
+ - lib/wechat/api/js_ticket.rb
170
170
  - lib/wechat/api/message.rb
171
171
  - lib/wechat/api/user.rb
172
172
  - lib/wechat/api/util.rb
173
173
  - lib/wechat/api/version.rb
174
- - lib/wechat/pay.rb
175
- - lib/wechat/pay/client.rb
176
- - lib/wechat/pay/redpack.rb
177
174
  - lib/wechat/qy.rb
178
175
  - lib/wechat/qy/client.rb
179
176
  - lib/wechat/qy/message.rb
180
177
  - spec/spec_helper.rb
181
178
  - spec/wechat/api/client_spec.rb
182
- - spec/wechat/pay/client_spec.rb
179
+ - spec/wechat/api/js_ticket_spec.rb
180
+ - spec/wechat/api/util_spec.rb
183
181
  - wechat-api.gemspec
184
182
  homepage: https://github.com/lazing/wechat-api
185
183
  licenses:
186
184
  - MIT
187
185
  metadata: {}
188
- post_install_message:
186
+ post_install_message:
189
187
  rdoc_options: []
190
188
  require_paths:
191
189
  - lib
@@ -201,10 +199,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
199
  version: '0'
202
200
  requirements: []
203
201
  rubygems_version: 3.0.6
204
- signing_key:
202
+ signing_key:
205
203
  specification_version: 4
206
204
  summary: Wechat API wrapper
207
205
  test_files:
208
206
  - spec/spec_helper.rb
209
207
  - spec/wechat/api/client_spec.rb
210
- - spec/wechat/pay/client_spec.rb
208
+ - spec/wechat/api/js_ticket_spec.rb
209
+ - spec/wechat/api/util_spec.rb
@@ -1,20 +0,0 @@
1
- name: Ruby
2
-
3
- on: [push]
4
-
5
- jobs:
6
- build:
7
-
8
- runs-on: ubuntu-latest
9
-
10
- steps:
11
- - uses: actions/checkout@v1
12
- - name: Set up Ruby 2.6
13
- uses: actions/setup-ruby@v1
14
- with:
15
- ruby-version: 2.6.x
16
- - name: Build and test with Rake
17
- run: |
18
- gem install bundler
19
- bundle install --jobs 4 --retry 3
20
- bundle exec rake
@@ -1,7 +0,0 @@
1
- require 'wechat/pay/client'
2
-
3
- module Wechat
4
- #
5
- module Pay
6
- end
7
- end
@@ -1,116 +0,0 @@
1
- require 'rest-client'
2
- require 'logger'
3
- require 'multi_json'
4
- require 'openssl'
5
- require 'nori'
6
- require 'gyoku'
7
-
8
- require 'wechat/pay/redpack'
9
-
10
- module Wechat
11
- module Pay
12
- class PayError < StandardError; end
13
- class NoAuthError < PayError; end
14
- class NotEnoughError < PayError; end
15
- class TimeLimitedError < PayError; end
16
- class MoneyLimitedError < PayError; end
17
-
18
- #
19
- class Client
20
- include Redpack
21
- BASE_URL = 'https://api.mch.weixin.qq.com'
22
- REQUIRED_OPTS = %w(key password cert sign_key).map(&:to_sym).freeze
23
- ERRORS = {
24
- 'NO_AUTH' => NoAuthError,
25
- 'NOTENOUGH' => NotEnoughError,
26
- 'TIME_LIMITED' => TimeLimitedError,
27
- 'MONEY_LIMITED' => MoneyLimitedError
28
- }
29
-
30
- attr_accessor :logger
31
-
32
- def initialize(mch_id, wxappid, opts = {})
33
- @mch_id = mch_id
34
- @wxappid = wxappid
35
- @opts = Hash[opts.map { |k, v| [k.to_sym, v] }]
36
- unless (REQUIRED_OPTS - @opts.keys).empty?
37
- fail format('%s required', REQUIRED_OPTS.join(','))
38
- end
39
- @logger = Logger.new(STDOUT)
40
- rsa_setup
41
- @parser = Nori.new
42
- end
43
-
44
- def post(path, params)
45
- merged_params = merge(params)
46
- logger.debug { merged_params }
47
- resp = resource(path).post(xml(sign(merged_params)))
48
- handle(resp)
49
- rescue RestClient::ExceptionWithResponse => err
50
- raise PayError, err.response
51
- end
52
-
53
- private
54
-
55
- def handle(resp)
56
- response = parser.parse(resp)
57
- check(response)
58
- response
59
- end
60
-
61
- def check(r)
62
- return if r['xml']['result_code'] == 'SUCCESS'
63
- handle_error(r['xml']['err_code'], r)
64
- end
65
-
66
- def handle_error(error_code, response)
67
- fail ERRORS[error_code] || PayError, response.inspect
68
- end
69
-
70
- def rsa_setup
71
- @rsa_key = OpenSSL::PKey::RSA.new @opts[:key], @opts[:password]
72
- @rsa_cert = OpenSSL::X509::Certificate.new @opts[:cert]
73
- rescue StandardError => e
74
- logger.error { e.inspect }
75
- end
76
-
77
- def resource(path)
78
- RestClient.log = logger
79
- RestClient::Resource.new\
80
- [BASE_URL, path].join,
81
- ssl_client_key: @rsa_key,
82
- ssl_client_cert: @rsa_cert,
83
- verify_ssl: OpenSSL::SSL::VERIFY_NONE
84
- end
85
-
86
- def xml(hash)
87
- Gyoku.xml({ xml: hash }, key_converter: :none)
88
- end
89
-
90
- def sign(params)
91
- ordered = trim_and_sort(params)
92
- keystr = format('key=%s', @opts[:sign_key])
93
- origin =
94
- ordered.map { |k, v| [k, v].join('=') }.push(keystr).join('&')
95
- sign = Digest::MD5.hexdigest(origin).upcase
96
- logger.debug { format('origin: %s, sign: %s', origin, sign) }
97
- params.merge(sign: sign)
98
- end
99
-
100
- def trim_and_sort(params)
101
- params.delete_if { |_k, v| v.blank? }
102
- Hash[params.sort]
103
- end
104
-
105
- def merge(params)
106
- params.reverse_merge\
107
- mch_id: @mch_id,
108
- nonce_str: nonce_str
109
- end
110
-
111
- def nonce_str
112
- SecureRandom.hex
113
- end
114
- end
115
- end
116
- end
@@ -1,39 +0,0 @@
1
- require 'securerandom'
2
-
3
- module Wechat
4
- module Pay
5
- #
6
- module Redpack
7
- def redpack(transaction_id, openid, params = {})
8
- post\
9
- '/mmpaymkttransfers/sendredpack',
10
- params.merge(
11
- mch_billno: tran_id(transaction_id),
12
- wxappid: @wxappid, re_openid: openid
13
- )
14
- end
15
-
16
- def group_redpack(transaction_id, openid, params = {})
17
- post\
18
- '/mmpaymkttransfers/sendgroupredpack',
19
- params.merge(
20
- mch_billno: tran_id(transaction_id),
21
- wxappid: @wxappid, re_openid: openid
22
- )
23
- end
24
-
25
- def redpack_info(transaction_id)
26
- post\
27
- '/mmpaymkttransfers/gethbinfo',
28
- mch_billno: tran_id(transaction_id),
29
- bill_type: 'MCHT', appid: @wxappid
30
- end
31
-
32
- private
33
-
34
- def tran_id(origin)
35
- format('%s%s', @mch_id, origin)
36
- end
37
- end
38
- end
39
- end
@@ -1,24 +0,0 @@
1
- require File.expand_path('../../../spec_helper', __FILE__)
2
-
3
- RSpec.describe Wechat::Pay::Client do
4
- subject do
5
- described_class.new\
6
- 'mch_id', 'wxappid',
7
- key: 'key', password: 'password', cert: 'cert',
8
- sign_key: 'sign_key'
9
- end
10
-
11
- it :trim_and_sort do
12
- expect(
13
- subject.send(:trim_and_sort, b: 2, c: 3, a: 1).values
14
- ).to start_with(1)
15
- end
16
-
17
- it :sign do
18
- expect(subject.send(:sign, b: 2, a: 1)).to have_key(:sign)
19
- end
20
-
21
- it :xml do
22
- expect(subject.send(:xml, b_1: 2, a: 1)).to match(/xml/)
23
- end
24
- end