Skip to content

Instantly share code, notes, and snippets.

@caiguanhao
Created July 6, 2021 15:44
Show Gist options
  • Save caiguanhao/8750fa7095d800f3ec25c3ce02fe0e39 to your computer and use it in GitHub Desktop.
Save caiguanhao/8750fa7095d800f3ec25c3ce02fe0e39 to your computer and use it in GitHub Desktop.
wx_pay hotfix
require 'http'
require 'openssl'
require 'digest/md5'
module WxPay
class << self
attr_accessor :appid, :mch_id, :key, :appsecret
attr_reader :apiclient_cert, :apiclient_key
def apiclient_cert=(cert)
@apiclient_cert = OpenSSL::X509::Certificate.new(cert)
end
def apiclient_key=(key)
@apiclient_key = OpenSSL::PKey::RSA.new(key)
end
end
module Service
GATEWAY_URL = 'https://api.mch.weixin.qq.com'.freeze
def self.invoke_unifiedorder(params, options = {})
params = {
appid: options.delete(:appid) || WxPay.appid,
mch_id: options.delete(:mch_id) || WxPay.mch_id,
key: options.delete(:key) || WxPay.key,
nonce_str: SecureRandom.uuid.tr('-', '')
}.merge(params)
WxPay::Result.new(Hash.from_xml(invoke_remote('/pay/unifiedorder', make_payload(params), options)))
end
def self.authenticate_from_weapp(js_code, options = {})
payload = {
appid: options.delete(:appid) || WxPay.appid,
secret: options.delete(:appsecret) || WxPay.appsecret,
js_code: js_code,
grant_type: 'authorization_code'
}
url = 'https://api.weixin.qq.com/sns/jscode2session'
::JSON.parse(HTTP.timeout(5).get(url, params: payload).to_s, quirks_mode: true)
end
def self.generate_js_pay_req(params, options = {})
params = {
appId: options.delete(:appid) || WxPay.appid,
package: "prepay_id=#{params.delete(:prepayid)}",
key: options.delete(:key) || WxPay.key,
nonceStr: params.delete(:noncestr),
timeStamp: Time.now.to_i.to_s,
signType: 'MD5'
}.merge(params)
params[:paySign] = WxPay::Sign.generate(params)
params
end
def self.invoke_refund(params, options = {})
params = {
appid: options.delete(:appid) || WxPay.appid,
mch_id: options.delete(:mch_id) || WxPay.mch_id,
key: options.delete(:key) || WxPay.key,
nonce_str: SecureRandom.uuid.tr('-', ''),
}.merge(params)
params[:op_user_id] ||= params[:mch_id]
options = {
ssl_context: OpenSSL::SSL::SSLContext.new.tap do |ctx|
ctx.set_params(
cert: options.delete(:apiclient_cert) || WxPay.apiclient_cert,
key: options.delete(:apiclient_key) || WxPay.apiclient_key,
)
# ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
}.merge(options)
WxPay::Result.new(Hash.from_xml(invoke_remote('/secapi/pay/refund', make_payload(params), options)))
end
def self.refund_query(params, options = {})
params = {
appid: options.delete(:appid) || WxPay.appid,
mch_id: options.delete(:mch_id) || WxPay.mch_id,
key: options.delete(:key) || WxPay.key,
nonce_str: SecureRandom.uuid.tr('-', '')
}.merge(params)
WxPay::Result.new(Hash.from_xml(invoke_remote('/pay/refundquery', make_payload(params), options)))
end
def self.invoke_remote(url, payload, options = {})
gateway_url = options.delete(:gateway_url) || GATEWAY_URL
url = "#{gateway_url}#{url}"
options[:body] = payload
HTTP.timeout(5).headers(content_type: 'application/xml').post(url, options).to_s
end
def self.make_payload(params, sign_type = WxPay::Sign::SIGN_TYPE_MD5)
sign = WxPay::Sign.generate(params, sign_type)
"<xml>#{params.except(:key).sort.map { |k, v| "<#{k}>#{v}</#{k}>" }.join}<sign>#{sign}</sign></xml>"
end
end
class Result < ::Hash
SUCCESS_FLAG = 'SUCCESS'.freeze
def initialize(result)
super nil # Or it will call `super result`
self[:raw] = result
if result['xml'].class == Hash
result['xml'].each_pair do |k, v|
self[k] = v
end
end
end
def success?
self['return_code'] == SUCCESS_FLAG && self['result_code'] == SUCCESS_FLAG
end
end
module Sign
SIGN_TYPE_MD5 = 'MD5'
SIGN_TYPE_HMAC_SHA256 = 'HMAC-SHA256'
def self.generate(params, sign_type = SIGN_TYPE_MD5)
key = params.delete(:key)
new_key = params["key"] #after
key = params.delete("key") if params["key"] #after
query = params.sort.map do |k, v|
"#{k}=#{v}" if v.to_s != ''
end.compact.join('&')
string_sign_temp = "#{query}&key=#{key || new_key || WxPay.key}" #after
if sign_type == SIGN_TYPE_MD5
Digest::MD5.hexdigest(string_sign_temp).upcase
elsif sign_type == SIGN_TYPE_HMAC_SHA256
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), key, string_sign_temp).upcase
else
raise Exception, "WxPay Warn: unknown sign_type : #{sign_type}"
end
end
def self.verify?(params, options = {})
params = params.dup
params["appid"] = options[:appid] if options[:appid]
params["mch_id"] = options[:mch_id] if options[:mch_id]
params["key"] = options[:key] if options[:key]
sign = params.delete('sign') || params.delete(:sign)
generate(params, options[:sign_type] || SIGN_TYPE_MD5) == sign
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment