class Wechatpay::Api::V3::Client

Constants

SCHEMA

Attributes

appid[R]
key[R]
logger[RW]
mch_id[R]
rsa_key[R]
serial_no[R]
site[RW]

Public Class Methods

new(appid, mch_id, **opts) click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 18
def initialize(appid, mch_id, **opts)
  @appid = appid
  @mch_id = mch_id
  @rsa_key = OpenSSL::PKey::RSA.new opts[:cert] if opts[:cert]
  @serial_no = opts[:cert_no]
  @key = opts[:key]
  @site = opts[:site] || 'https://api.mch.weixin.qq.com'

  @logger = Logger.new(STDOUT)
end

Public Instance Methods

authorization_header(http_method, path, body) click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 97
def authorization_header(http_method, path, body)
  [SCHEMA, authorization_params(http_method, path, body)].join ' '
end
authorization_params(http_method, path, body) click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 101
def authorization_params(http_method, path, body)
  timestamp = Time.now.to_i
  rnd = SecureRandom.hex
  signature = sign_with(body, http_method, path, timestamp, rnd)
  [
    "mchid=\"#{mch_id}\"", "serial_no=\"#{serial_no}\"", "nonce_str=\"#{rnd}\"",
    "timestamp=\"#{timestamp}\"", "signature=\"#{signature}\""
  ].join(',')
end
cert() click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 65
def cert
  Wechatpay::Api::V3::Cert.instance.load || update_certs
end
connection() click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 29
def connection
  Faraday.new(url: @site) do |conn|
    conn.request :retry
    conn.response :logger
    # conn.response :raise_error
    conn.adapter :net_http
  end
end
dechipher(data, nonce, auth_data) click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 119
def dechipher(data, nonce, auth_data)
  chipher = OpenSSL::Cipher.new 'aes-256-gcm'
  chipher.decrypt
  chipher.key = key
  chipher.iv = nonce
  chipher.padding = 0
  chipher.auth_data = auth_data
  chipher.auth_tag = data[-16, 16]
  chipher
end
decrypt(cipher_text, nonce, auth_data) click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 111
def decrypt(cipher_text, nonce, auth_data)
  raise :cipher_text_invalid if (cipher_text.try(:length) || 0) < 16

  data = Base64.strict_decode64(cipher_text)
  dec = dechipher(data, nonce, auth_data)
  dec.update(data[0, data.length - 16]).tap { |s| logger.debug "DEC: #{s}" } + dec.final
end
get(path, params = nil) click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 38
def get(path, params = nil)
  resp = connection.get(path, params) do |req|
    path = req.path
    path = [req.path, Faraday::Utils.build_query(req.params)].join('?') unless params.nil? || params.empty?
    req.headers['Authorization'] = authorization_header('GET', path, nil)
    req.headers['Accept'] = 'application/json'
  end
  handle resp
end
handle(resp) click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 77
def handle(resp)
  logger.debug { "HANDLE RESPONSE: #{resp.inspect}" }
  data = resp.body
  raise :empty_body unless data && !data.empty?

  MultiJson.load data, symbolize_keys: true
end
post(path, data, **headers) click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 48
def post(path, data, **headers)
  body = data.is_a?(Hash) ? MultiJson.dump(data) : data
  resp = connection.post(path, body, headers) do |req|
    req.headers['Authorization'] = authorization_header('POST', path, body)
    req.headers['Accept'] = 'application/json'
  end
  handle resp
end
sign_content(content) click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 91
def sign_content(content)
  digest = OpenSSL::Digest::SHA256.new
  signed = rsa_key.sign(digest, content)
  Base64.strict_encode64 signed
end
sign_with(body, method, path, timestamp, rnd) click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 85
def sign_with(body, method, path, timestamp, rnd)
  str = [method, path, timestamp, rnd, body].join("\n") + "\n"
  logger.debug { "Sign Content: #{str.inspect}" }
  sign_content(str)
end
update_certs() click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 69
def update_certs
  certs = get('/v3/certificates')[:data]
  certs.map do |raw|
    Wechatpay::Api::V3::Cert.instance.update(raw, &method(:decrypt))
  end
  Wechatpay::Api::V3::Cert.instance
end
verify(headers, body) click to toggle source
# File lib/wechatpay/api/v3/client.rb, line 57
def verify(headers, body)
  sha256 = OpenSSL::Digest::SHA256.new
  key = cert.certificate.public_key
  sign = Base64.strict_decode64(headers['Wechatpay-Signature'])
  data = %w[Wechatpay-Timestamp Wechatpay-Nonce].map { |k| headers[k] }
  key.verify sha256, sign, data.append(body).join("\n") + "\n"
end