alipay-easysdk-ruby 1.0.1 → 1.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2b04a16730b90788a7d0c75911eb49750f253f1a40cf6efab4a81696a31cf9b
4
- data.tar.gz: c66dfd5f038def8e6462b1cf2faa61f2f5fd42c42b2eea9b9426977b123a3eb9
3
+ metadata.gz: 9fbf7bcd464b1d9f4c42aaec6030ba6dfc9a4beda7eb5c3ea2587722b2fdff01
4
+ data.tar.gz: 43c10156f010ca3f98696361f9d9a07478bd1ea44c1533a086f5c2c066956c9b
5
5
  SHA512:
6
- metadata.gz: a97a34c94daf4056fe0b7d102db422bc3080c16f74f19b1e438f1e8e5993dc3288b5a552854d6e0898185f9f40609f529956250e0a363f1622a5a2cbd40593dd
7
- data.tar.gz: 28dc7cda0b59867844a27ca7871ee27cbcbf5bfc3b5908ec17c522b683e56300bd79a38f916ef87b3b275772264b715133ff344644a550e166831dc657ea2362
6
+ metadata.gz: d10e78d464ea90c15a6b9e17bcbbf1e6414fb3a842eda95f2662e69e3de245e6209214b096b8b3dd38f8bd0daefcd4c12b664b8947de02feb544b83f0be41b8e
7
+ data.tar.gz: fddc8561482df008189ba3cfcd93fc3751e1ea192d07c9008f201ee6c211f780da96ddd2de9d90395c48d575481e8f50c766a63e0792b05c1c55f5992f95d728
data/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ ## 1.0.3 - 2024-06-06
4
+
5
+ - Add `payment/app` client and response model to match PHP EasySDK behaviour.
6
+ - Align kernel utilities (Config, Factory, EasySDKKernel, PageUtil, AES, Signer, JsonUtil, ResponseChecker) with the PHP implementation.
7
+ - Update Payment::Common/Page/Wap clients and models to PHP parity while retaining the custom `payment_url` extension.
8
+ - Refresh parity fixtures, expand test coverage, and remove the deprecated `examples/` directory.
9
+
10
+ ## 1.0.2 - 2024-04-15
11
+
12
+ - Align `payment/page`, `payment/wap`, and `payment/common` dependencies with the PHP EasySDK implementation.
13
+ - Extend `Kernel::Config` to support certificate paths, proxy, notify URL, and encryption settings used by PHP clients.
14
+ - Introduce certificate environment utilities to derive certificate serial numbers and cached public keys.
15
+ - Update `Payment::Common::Client` to honour proxy and SSL settings and improve parity with PHP HTTP behaviour.
16
+ - Add comprehensive test coverage for certificate handling, extended configuration, and HTTP proxy behaviour.
17
+
18
+ ## 1.0.1 - 2024-03-01
19
+
20
+ - Initial public release of the Ruby EasySDK port.
@@ -4,41 +4,39 @@ module Alipay
4
4
  module EasySDK
5
5
  module Kernel
6
6
  module AlipayConstants
7
- DEFAULT_CHARSET = "UTF-8"
8
-
9
- # 签名类型
10
- SIGN_TYPE_RSA = "RSA"
11
- SIGN_TYPE_RSA2 = "RSA2"
7
+ # Config键名(对齐PHP版)
8
+ PROTOCOL_CONFIG_KEY = "protocol"
9
+ HOST_CONFIG_KEY = "gatewayHost"
10
+ ALIPAY_CERT_PATH_CONFIG_KEY = "alipayCertPath"
11
+ MERCHANT_CERT_PATH_CONFIG_KEY = "merchantCertPath"
12
+ ALIPAY_ROOT_CERT_PATH_CONFIG_KEY = "alipayRootCertPath"
13
+ SIGN_TYPE_CONFIG_KEY = "signType"
14
+ NOTIFY_URL_CONFIG_KEY = "notifyUrl"
12
15
 
13
- # 字段常量
14
- APP_ID_FIELD = "app_id"
15
- METHOD_FIELD = "method"
16
- FORMAT_FIELD = "format"
17
- TIMESTAMP_FIELD = "timestamp"
18
- VERSION_FIELD = "version"
19
- SIGN_TYPE_FIELD = "sign_type"
20
- SIGN_FIELD = "sign"
16
+ # 与网关交互使用的字段
21
17
  BIZ_CONTENT_FIELD = "biz_content"
22
- CHARSET_FIELD = "charset"
23
- BODY_FIELD = "body"
24
18
  ALIPAY_CERT_SN_FIELD = "alipay_cert_sn"
19
+ SIGN_FIELD = "sign"
20
+ BODY_FIELD = "http_body"
25
21
  NOTIFY_URL_FIELD = "notify_url"
26
- NOTIFY_URL_CONFIG_KEY = "notifyUrl"
27
- PROTOCOL_CONFIG_KEY = "protocol"
28
- HOST_CONFIG_KEY = "gatewayHost"
22
+ METHOD_FIELD = "method"
29
23
  RESPONSE_SUFFIX = "_response"
30
24
  ERROR_RESPONSE = "error_response"
31
25
 
32
- # 默认值
26
+ DEFAULT_CHARSET = "UTF-8"
33
27
  DEFAULT_FORMAT = "json"
34
28
  DEFAULT_VERSION = "1.0"
35
- DEFAULT_SIGN_TYPE = SIGN_TYPE_RSA2
29
+ DEFAULT_SIGN_TYPE = "RSA2"
30
+
31
+ SIGN_TYPE_RSA = "RSA"
32
+ SIGN_TYPE_RSA2 = "RSA2"
33
+ RSA2 = "RSA2"
34
+ SHA_256_WITH_RSA = "SHA256WithRSA"
35
+ RSA = "RSA"
36
36
 
37
- # 请求方式
38
37
  GET = "GET"
39
38
  POST = "POST"
40
39
 
41
- # SDK信息
42
40
  SDK_VERSION = "alipay-easysdk-ruby-#{Alipay::EasySDK::VERSION}"
43
41
  end
44
42
  end
@@ -0,0 +1,35 @@
1
+ require 'openssl'
2
+ require 'digest/md5'
3
+
4
+ require_relative 'util/ant_certification_util'
5
+
6
+ module Alipay
7
+ module EasySDK
8
+ module Kernel
9
+ class CertEnvironment
10
+ attr_reader :root_cert_sn, :merchant_cert_sn
11
+
12
+ def initialize
13
+ @cert_util = Util::AntCertificationUtil.new
14
+ @root_cert_sn = nil
15
+ @merchant_cert_sn = nil
16
+ @cached_alipay_public_key = nil
17
+ end
18
+
19
+ def setup(merchant_cert_path, alipay_cert_path, alipay_root_cert_path)
20
+ if [merchant_cert_path, alipay_cert_path, alipay_root_cert_path].any? { |path| path.nil? || path.to_s.strip.empty? }
21
+ raise RuntimeError, '证书参数merchantCertPath、alipayCertPath或alipayRootCertPath设置不完整。'
22
+ end
23
+
24
+ @root_cert_sn = @cert_util.root_cert_sn(alipay_root_cert_path)
25
+ @merchant_cert_sn = @cert_util.cert_sn(merchant_cert_path)
26
+ @cached_alipay_public_key = @cert_util.public_key(alipay_cert_path)
27
+ end
28
+
29
+ def cached_alipay_public_key
30
+ @cached_alipay_public_key
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,36 +1,107 @@
1
- require_relative 'alipay_constants'
2
-
3
1
  module Alipay
4
2
  module EasySDK
5
3
  module Kernel
6
4
  class Config
7
- attr_accessor :protocol, :gateway_host, :app_id, :merchant_private_key,
8
- :alipay_public_key, :sign_type, :charset, :format, :version,
9
- :merchant_cert_sn, :alipay_root_cert_sn, :notify_url
10
-
11
- def initialize(options = {})
12
- @protocol = options[:protocol] || 'https'
13
- @gateway_host = options[:gateway_host] || 'openapi.alipay.com/gateway.do'
14
- @app_id = options[:app_id]
15
- @merchant_private_key = options[:merchant_private_key]
16
- @alipay_public_key = options[:alipay_public_key]
17
- @sign_type = options[:sign_type] || AlipayConstants::DEFAULT_SIGN_TYPE
18
- @charset = options[:charset] || AlipayConstants::DEFAULT_CHARSET
19
- @format = options[:format] || AlipayConstants::DEFAULT_FORMAT
20
- @version = options[:version] || AlipayConstants::DEFAULT_VERSION
21
- @merchant_cert_sn = options[:merchant_cert_sn]
22
- @alipay_root_cert_sn = options[:alipay_root_cert_sn]
23
- @notify_url = options[:notify_url]
5
+ attr_accessor :protocol,
6
+ :gatewayHost,
7
+ :appId,
8
+ :signType,
9
+ :alipayPublicKey,
10
+ :merchantPrivateKey,
11
+ :merchantCertPath,
12
+ :alipayCertPath,
13
+ :alipayRootCertPath,
14
+ :merchantCertSN,
15
+ :alipayCertSN,
16
+ :alipayRootCertSN,
17
+ :notifyUrl,
18
+ :encryptKey,
19
+ :httpProxy,
20
+ :ignoreSSL
21
+
22
+ def initialize(options = nil)
23
+ assign_attributes(options) if options
24
+ end
25
+
26
+ # 下划线风格的访问器保持向后兼容
27
+ alias_method :gateway_host, :gatewayHost
28
+ alias_method :gateway_host=, :gatewayHost=
29
+
30
+ alias_method :app_id, :appId
31
+ alias_method :app_id=, :appId=
32
+
33
+ alias_method :sign_type, :signType
34
+ alias_method :sign_type=, :signType=
35
+
36
+ alias_method :alipay_public_key, :alipayPublicKey
37
+ alias_method :alipay_public_key=, :alipayPublicKey=
38
+
39
+ alias_method :merchant_private_key, :merchantPrivateKey
40
+ alias_method :merchant_private_key=, :merchantPrivateKey=
41
+
42
+ alias_method :merchant_cert_path, :merchantCertPath
43
+ alias_method :merchant_cert_path=, :merchantCertPath=
44
+
45
+ alias_method :alipay_cert_path, :alipayCertPath
46
+ alias_method :alipay_cert_path=, :alipayCertPath=
47
+
48
+ alias_method :alipay_root_cert_path, :alipayRootCertPath
49
+ alias_method :alipay_root_cert_path=, :alipayRootCertPath=
50
+
51
+ alias_method :merchant_cert_sn, :merchantCertSN
52
+ alias_method :merchant_cert_sn=, :merchantCertSN=
53
+
54
+ alias_method :alipay_cert_sn, :alipayCertSN
55
+ alias_method :alipay_cert_sn=, :alipayCertSN=
56
+
57
+ alias_method :alipay_root_cert_sn, :alipayRootCertSN
58
+ alias_method :alipay_root_cert_sn=, :alipayRootCertSN=
59
+
60
+ alias_method :notify_url, :notifyUrl
61
+ alias_method :notify_url=, :notifyUrl=
62
+
63
+ alias_method :encrypt_key, :encryptKey
64
+ alias_method :encrypt_key=, :encryptKey=
65
+
66
+ alias_method :http_proxy, :httpProxy
67
+ alias_method :http_proxy=, :httpProxy=
68
+
69
+ alias_method :ignore_ssl, :ignoreSSL
70
+ alias_method :ignore_ssl=, :ignoreSSL=
71
+
72
+ private
73
+
74
+ def assign_attributes(options)
75
+ options.each do |key, value|
76
+ setter = attribute_writer_for(key)
77
+ public_send(setter, value) if setter && respond_to?(setter, true)
78
+ end
79
+ end
80
+
81
+ def attribute_writer_for(key)
82
+ string_key = key.to_s
83
+ writer = "#{string_key}="
84
+ return writer if respond_to?(writer, true)
85
+
86
+ camel_case = underscore_to_camel(string_key)
87
+ writer = "#{camel_case}="
88
+ return writer if respond_to?(writer, true)
89
+
90
+ snake_case = camel_to_snake(string_key)
91
+ writer = "#{snake_case}="
92
+ return writer if respond_to?(writer, true)
93
+
94
+ nil
24
95
  end
25
96
 
26
- def gateway_url
27
- "#{@protocol}://#{@gateway_host}"
97
+ def underscore_to_camel(str)
98
+ str.split('_').inject do |memo, part|
99
+ memo + part.capitalize
100
+ end
28
101
  end
29
102
 
30
- def validate
31
- raise "app_id is required" if @app_id.nil? || @app_id.empty?
32
- raise "merchant_private_key is required" if @merchant_private_key.nil? || @merchant_private_key.empty?
33
- raise "alipay_public_key is required" if @alipay_public_key.nil? || @alipay_public_key.empty?
103
+ def camel_to_snake(str)
104
+ str.gsub(/([A-Z]+)/) { "_#{$1.downcase}" }.sub(/^_/, '')
34
105
  end
35
106
  end
36
107
  end
@@ -3,9 +3,12 @@ require_relative 'config'
3
3
  require_relative 'util/json_util'
4
4
  require_relative 'util/signer'
5
5
  require_relative 'util/sign_content_extractor'
6
+ require_relative 'util/aes'
7
+ require_relative 'util/page_util'
6
8
  require 'net/http'
7
9
  require 'uri'
8
10
  require 'cgi'
11
+ require 'json'
9
12
 
10
13
  module Alipay
11
14
  module EasySDK
@@ -38,28 +41,16 @@ module Alipay
38
41
  end
39
42
 
40
43
  def get_config(key)
41
- case key
42
- when 'protocol'
43
- @config.protocol
44
- when 'gatewayHost'
45
- @config.gateway_host
46
- when 'appId'
47
- @config.app_id
48
- when 'merchantPrivateKey'
49
- @config.merchant_private_key
50
- when 'alipayPublicKey'
51
- @config.alipay_public_key
52
- when 'signType'
53
- @config.sign_type
54
- when 'charset'
55
- @config.charset
56
- when 'format'
57
- @config.format
58
- when 'version'
59
- @config.version
60
- else
61
- nil
62
- end
44
+ method = key.to_s
45
+ return @config.public_send(method) if @config.respond_to?(method)
46
+
47
+ snake = method.gsub(/([A-Z]+)/) { "_#{$1.downcase}" }.sub(/^_/, '')
48
+ return @config.public_send(snake) if @config.respond_to?(snake)
49
+
50
+ camel = snake.split('_').inject { |memo, part| memo + part.capitalize }
51
+ return @config.public_send(camel) if camel && @config.respond_to?(camel)
52
+
53
+ nil
63
54
  end
64
55
 
65
56
  def get_sdk_version
@@ -77,9 +68,9 @@ module Alipay
77
68
  def read_as_json(response, method)
78
69
  response_body = response.body
79
70
  map = {}
80
- map['body'] = response_body
81
- map['method'] = method
82
- return map
71
+ map[AlipayConstants::BODY_FIELD] = response_body
72
+ map[AlipayConstants::METHOD_FIELD] = method
73
+ map
83
74
  end
84
75
 
85
76
  def get_random_boundary
@@ -89,11 +80,12 @@ module Alipay
89
80
  def generate_page(method, system_params, biz_params, text_params, sign)
90
81
  puts "[DEBUG] generate_page - sign长度: #{sign.length}" if ENV['DEBUG']
91
82
  signed_params = build_signed_params(system_params, biz_params, text_params, sign)
92
- if method == 'GET'
93
- return build_gateway_url(signed_params)
94
- elsif method == 'POST'
83
+ case method
84
+ when AlipayConstants::GET
85
+ build_gateway_url(signed_params)
86
+ when AlipayConstants::POST
95
87
  puts "[DEBUG] generate_page POST - sorted_map中sign长度: #{signed_params[AlipayConstants::SIGN_FIELD].length}" if ENV['DEBUG']
96
- return build_form(get_gateway_server_url, signed_params)
88
+ build_form(get_gateway_server_url, signed_params)
97
89
  else
98
90
  raise "不支持" + method
99
91
  end
@@ -110,8 +102,8 @@ module Alipay
110
102
 
111
103
  def get_alipay_cert_sn(resp_map)
112
104
  if !@config.merchant_cert_sn.nil? && !@config.merchant_cert_sn.empty?
113
- body = JSON.parse(resp_map['body'])
114
- alipay_cert_sn = body['alipay_cert_sn']
105
+ body = JSON.parse(resp_map[AlipayConstants::BODY_FIELD])
106
+ alipay_cert_sn = body[AlipayConstants::ALIPAY_CERT_SN_FIELD]
115
107
  return alipay_cert_sn
116
108
  end
117
109
  end
@@ -130,10 +122,10 @@ module Alipay
130
122
  end
131
123
 
132
124
  def verify(resp_map, alipay_public_key)
133
- resp = JSON.parse(resp_map['body'])
125
+ resp = JSON.parse(resp_map[AlipayConstants::BODY_FIELD])
134
126
  sign = resp[AlipayConstants::SIGN_FIELD]
135
127
  sign_content_extractor = Alipay::EasySDK::Kernel::Util::SignContentExtractor.new
136
- content = sign_content_extractor.get_sign_source_data(resp_map['body'], resp_map['method'])
128
+ content = sign_content_extractor.get_sign_source_data(resp_map[AlipayConstants::BODY_FIELD], resp_map[AlipayConstants::METHOD_FIELD])
137
129
  signer = Alipay::EasySDK::Kernel::Util::Signer.new
138
130
  return signer.verify(content, sign, alipay_public_key)
139
131
  end
@@ -158,18 +150,55 @@ module Alipay
158
150
  URI.encode_www_form(sorted_map)
159
151
  end
160
152
 
153
+ def to_multipart_request_body(text_params, file_params, boundary)
154
+ @text_params = text_params
155
+ @biz_params = nil
156
+ if text_params != nil && !@optional_text_params.empty?
157
+ @text_params = text_params.merge(@optional_text_params)
158
+ elsif text_params == nil
159
+ @text_params = @optional_text_params.dup
160
+ end
161
+
162
+ parts = []
163
+ (@text_params || {}).each do |key, value|
164
+ parts << build_multipart_text_part(boundary, key, value)
165
+ end
166
+
167
+ (file_params || {}).each do |key, path|
168
+ raise "文件#{path}不存在" unless File.exist?(path.to_s)
169
+ file_content = File.binread(path)
170
+ filename = File.basename(path)
171
+ parts << build_multipart_file_part(boundary, key, filename, file_content)
172
+ end
173
+
174
+ return nil if parts.empty?
175
+
176
+ parts << "--#{boundary}--\r\n"
177
+ parts.join
178
+ end
179
+
180
+ def aes_encrypt(content, encrypt_key)
181
+ aes = Alipay::EasySDK::Kernel::Util::AES.new
182
+ aes.aes_encrypt(content, encrypt_key)
183
+ end
184
+
185
+ def aes_decrypt(content, encrypt_key)
186
+ aes = Alipay::EasySDK::Kernel::Util::AES.new
187
+ aes.aes_decrypt(content, encrypt_key)
188
+ end
189
+
161
190
  def sort_map(random_map)
162
191
  return random_map
163
192
  end
164
193
 
165
194
  def to_resp_model(resp_map)
166
- body = resp_map['body']
167
- method_name = resp_map['method']
195
+ body = resp_map[AlipayConstants::BODY_FIELD]
196
+ method_name = resp_map[AlipayConstants::METHOD_FIELD]
168
197
  response_node_name = method_name.gsub(".", "_") + "_response"
169
198
 
170
199
  model = JSON.parse(body)
171
- if body.include?("error_response")
172
- result = model["error_response"]
200
+ if body.include?(AlipayConstants::ERROR_RESPONSE)
201
+ result = model[AlipayConstants::ERROR_RESPONSE]
173
202
  result['body'] = body
174
203
  else
175
204
  result = model[response_node_name]
@@ -216,15 +245,15 @@ module Alipay
216
245
  @biz_params = @optional_biz_params
217
246
  end
218
247
 
219
- json = Alipay::EasySDK::Kernel::Util::JsonUtil.new
220
- if @biz_params != nil
221
- biz_params = json.to_json_string(@biz_params)
222
- end
223
- sorted_map = system_params || {}
224
- if !biz_params.nil? && !biz_params.empty?
225
- # 模拟PHP json_encode($bizParams, JSON_UNESCAPED_UNICODE)
226
- json_string = JSON.generate(JSON.parse(biz_params)).gsub('/', "\\/")
227
- sorted_map[AlipayConstants::BIZ_CONTENT_FIELD] = json_string
248
+ json_util = Alipay::EasySDK::Kernel::Util::JsonUtil.new
249
+ biz_content = json_util.to_json_string(@biz_params) unless @biz_params.nil?
250
+
251
+ sorted_map = (system_params || {}).dup
252
+ if !biz_content.nil?
253
+ unless biz_content.respond_to?(:empty?) && biz_content.empty?
254
+ serialized = JSON.generate(biz_content, ascii_only: false).gsub('/', '\\/')
255
+ sorted_map[AlipayConstants::BIZ_CONTENT_FIELD] = serialized
256
+ end
228
257
  end
229
258
  if !@text_params.nil? && !@text_params.empty?
230
259
  if !sorted_map.empty?
@@ -233,15 +262,19 @@ module Alipay
233
262
  sorted_map = @text_params
234
263
  end
235
264
  end
236
- if get_config('notify_url') != nil
237
- sorted_map['notify_url'] = get_config('notify_url')
265
+ notify_value = get_config(AlipayConstants::NOTIFY_URL_CONFIG_KEY)
266
+ if !notify_value.nil? && notify_value.to_s.strip != ''
267
+ sorted_map[AlipayConstants::NOTIFY_URL_FIELD] = notify_value
238
268
  end
239
269
  return sorted_map
240
270
  end
241
271
 
242
272
  def get_sign_content(params)
243
273
  # 模拟PHP的ksort
244
- sorted_params = params.sort_by { |k, _| k.to_s }.to_h
274
+ normalized = params.each_with_object({}) do |(key, value), acc|
275
+ acc[key.to_s] = value
276
+ end
277
+ sorted_params = normalized.sort.to_h
245
278
 
246
279
  string_to_be_signed = ""
247
280
  i = 0
@@ -261,7 +294,9 @@ module Alipay
261
294
  end
262
295
 
263
296
  def get_gateway_server_url
264
- return get_config('protocol') + '://' + get_config('gatewayHost').gsub('/gateway.do', '') + '/gateway.do'
297
+ protocol = get_config(AlipayConstants::PROTOCOL_CONFIG_KEY)
298
+ host = get_config(AlipayConstants::HOST_CONFIG_KEY)
299
+ return protocol + '://' + host.gsub('/gateway.do', '') + '/gateway.do'
265
300
  end
266
301
 
267
302
  def check_empty(value)
@@ -302,25 +337,8 @@ module Alipay
302
337
  puts "[DEBUG] build_form - 原始params keys: #{params.keys.join(', ')}" if ENV['DEBUG']
303
338
  puts "[DEBUG] build_form - sign长度: #{params[AlipayConstants::SIGN_FIELD].length}" if ENV['DEBUG'] && params[AlipayConstants::SIGN_FIELD]
304
339
 
305
- # 完全按照PHP版本的PageUtil::buildForm方法:过滤空值参数
306
- form_fields = ""
307
- params.each do |key, val|
308
- if !check_empty(val) # 与PHP版本的checkEmpty逻辑完全一致
309
- # 按照PHP版本:将单引号替换为&amp;apos;
310
- escaped_val = val.to_s.gsub("'", "&apos;")
311
- form_fields += "<input type='hidden' name='#{key}' value='#{escaped_val}'/>"
312
- end
313
- end
314
-
315
- # 完全按照PHP版本:在action URL中添加charset参数
316
- action_url = "#{url}?charset=#{AlipayConstants::DEFAULT_CHARSET}"
317
-
318
- <<~HTML
319
- <form id='alipaysubmit' name='alipaysubmit' action='#{action_url}' method='POST'>
320
- #{form_fields}
321
- <input type='submit' value='ok' style='display:none;'></form>
322
- <script>document.forms['alipaysubmit'].submit();</script>
323
- HTML
340
+ page_util = Alipay::EasySDK::Kernel::Util::PageUtil.new
341
+ page_util.build_form(url, params)
324
342
  end
325
343
 
326
344
  def build_signed_params(system_params, biz_params, text_params, sign)
@@ -330,10 +348,25 @@ module Alipay
330
348
  end
331
349
 
332
350
  def build_gateway_url(signed_params)
333
- base_url = get_gateway_server_url + "?charset=#{AlipayConstants::DEFAULT_CHARSET}"
351
+ base_url = get_gateway_server_url
334
352
  query = build_query_string(signed_params)
335
353
  return base_url if query.nil? || query.empty?
336
- base_url + '&' + query
354
+ base_url + '?' + query
355
+ end
356
+
357
+ def build_multipart_text_part(boundary, key, value)
358
+ "--#{boundary}\r\n" \
359
+ "Content-Disposition: form-data; name=\"#{key}\"\r\n" \
360
+ "\r\n" \
361
+ "#{value}\r\n"
362
+ end
363
+
364
+ def build_multipart_file_part(boundary, key, filename, content)
365
+ "--#{boundary}\r\n" \
366
+ "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{filename}\"\r\n" \
367
+ "Content-Type: application/octet-stream\r\n" \
368
+ "\r\n" \
369
+ "#{content}\r\n"
337
370
  end
338
371
  end
339
372
  end
@@ -1,6 +1,8 @@
1
1
  require_relative 'alipay_constants'
2
2
  require_relative 'config'
3
+ require_relative 'cert_environment'
3
4
  require_relative 'easy_sdk_kernel'
5
+ require_relative '../payment/app/client'
4
6
  require_relative '../payment/wap/client'
5
7
  require_relative '../payment/page/client'
6
8
  require_relative '../payment/common/client'
@@ -12,10 +14,13 @@ module Alipay
12
14
  class ConfigurationNotSetError < StandardError; end
13
15
 
14
16
  class << self
15
- def set_options(options = nil)
17
+ def set_options(options)
18
+ raise ArgumentError, '配置参数不能为空' if options.nil?
19
+ return @instance if defined?(@instance) && @instance
20
+
16
21
  config = normalize_config(options)
17
22
  initialize_context(config)
18
- @instance
23
+ @instance = self
19
24
  end
20
25
 
21
26
  alias setOptions set_options
@@ -26,14 +31,18 @@ module Alipay
26
31
  @config
27
32
  end
28
33
 
29
- def payment
30
- ensure_context_set!
31
- @payment
32
- end
34
+ def payment
35
+ ensure_context_set!
36
+ @payment
37
+ end
33
38
 
34
- def wap
35
- payment.wap
36
- end
39
+ def app
40
+ payment.app
41
+ end
42
+
43
+ def wap
44
+ payment.wap
45
+ end
37
46
 
38
47
  def page
39
48
  payment.page
@@ -55,21 +64,30 @@ module Alipay
55
64
  private
56
65
 
57
66
  def normalize_config(options)
58
- raise ArgumentError, '配置参数不能为空' if options.nil?
59
67
  return options if options.is_a?(Config)
60
68
  Config.new(options)
61
69
  end
62
70
 
63
71
  def initialize_context(config)
72
+ apply_cert_environment(config)
64
73
  @config = config
65
74
  @kernel = EasySDKKernel.new(config)
66
75
  @payment = Payment.new(@kernel)
67
- @instance = self
68
76
  end
69
77
 
70
78
  def ensure_context_set!
71
79
  raise ConfigurationNotSetError, '请先调用Factory.set_options(config)设置SDK配置' unless @config
72
80
  end
81
+
82
+ def apply_cert_environment(config)
83
+ return if config.alipayCertPath.nil? || config.alipayCertPath.to_s.strip.empty?
84
+
85
+ cert_env = CertEnvironment.new
86
+ cert_env.setup(config.merchantCertPath, config.alipayCertPath, config.alipayRootCertPath)
87
+ config.merchantCertSN = cert_env.merchant_cert_sn
88
+ config.alipayRootCertSN = cert_env.root_cert_sn
89
+ config.alipayPublicKey ||= cert_env.cached_alipay_public_key
90
+ end
73
91
  end
74
92
 
75
93
  class Payment
@@ -77,6 +95,10 @@ module Alipay
77
95
  @kernel = kernel
78
96
  end
79
97
 
98
+ def app
99
+ @app ||= Alipay::EasySDK::Payment::App::Client.new(@kernel)
100
+ end
101
+
80
102
  def wap
81
103
  @wap ||= Alipay::EasySDK::Payment::Wap::Client.new(@kernel)
82
104
  end