0.0
The project is in a healthy, maintained state
通联支付 CNP 跨境信用卡收单 Ruby SDK
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

>= 1.0, < 3.0
 Project Readme

AllinpayCnp

通联支付 CNP 跨境信用卡收单 Ruby SDK。

安装

Gemfile

gem 'allinpay_cnp', '~> 0.1.6'

本地安装

cd allinpay_cnp
gem build allinpay_cnp.gemspec
gem install allinpay_cnp-0.1.6.gem

配置

Rails 项目

创建 config/initializers/allinpay_cnp.rb:

AllinpayCnp.configure do |config|
  config.merchant_id = Rails.application.credentials.dig(:allinpay, :merchant_id)
  config.private_key = Rails.application.credentials.dig(:allinpay, :private_key)
  config.public_key  = Rails.application.credentials.dig(:allinpay, :public_key)
  config.inst_no     = Rails.application.credentials.dig(:allinpay, :inst_no)
  config.environment = Rails.env.production? ? :production : :test
  config.timeout     = 30
  config.logger      = Rails.logger
end

普通 Ruby 项目

require 'allinpay_cnp'

AllinpayCnp.configure do |config|
  config.merchant_id = '086310030670001'
  config.private_key = File.read('private_key.pem')
  config.public_key  = File.read('public_key.pem')
  config.inst_no     = '00000001'  # 机构模式下必填,直连商户模式不填
  config.environment = :test       # :test 或 :production
  config.timeout     = 30
  config.logger      = Logger.new($stdout)
end

配置项

配置项 类型 必填 说明
merchant_id String 条件必填 商户号。若每次调用时都传入 merchant_no,可不在全局配置
private_key String 条件必填 商户私钥 (PEM 格式),用于请求签名。若每次调用时都传入 private_key,可不在全局配置
public_key String 系统公钥 (PEM 格式),用于验证回调/应答签名。亦可在调用时通过 public_key / verify_callback(public_key:) 传入
inst_no String 机构号,机构模式下必填,直连商户模式留空
environment Symbol :test (默认) 或 :production
timeout Integer 请求超时时间,默认 30 秒
logger Logger 日志对象

多商户模式下可以不在全局配置 merchant_id / private_key,而是在每次调用时按商户传入。详见 多商户模式。

密钥说明

密钥 用途 对应配置
商户私钥 请求时计算签名 config.private_key
系统公钥 接收应答/异步通知时验签 config.public_key
商户公钥 上传至通联后台,通联用来验证你的请求签名 不需要配置到代码中

使用方法

获取客户端

client = AllinpayCnp.client

统一收银台支付 (Unified Pay)

跳转到通联支付页面完成支付:

response = client.unified_pay(
  access_order_id: "ORDER_#{Time.now.to_i}",
  amount: '100.00',
  currency: 'HKD',
  urls: {
    notify_url: 'https://your-domain.com/webhooks/allinpay',
    return_url: 'https://your-domain.com/payments/complete'
  },
  email: 'customer@example.com',
  language: 'zh-hant',  # 可选: zh-hant, zh-hans, en
  product_info: [       # 商品信息,可选,会以 JSON 字符串形式发送
    { sku: 'SKU001', productName: 'Test Product', price: '100.00', quantity: '1' }
  ],
  shipping: {
    first_name: 'Peter',
    last_name: 'Zhang',
    address1: '123 Test Street',
    city: 'Hong Kong',
    country: 'HK',
    zip_code: '000000',
    phone: '12345678'
  },
  billing: {
    first_name: 'Peter',
    last_name: 'Zhang',
    address1: '123 Test Street',
    city: 'Hong Kong',
    country: 'HK',
    zip_code: '000000',
    phone: '12345678'
  }
)

if response.success?
  redirect_to response.payment_url
else
  puts "Error: #{response.result_desc}"
end

查询订单 (Query)

response = client.query(ori_access_order_id: 'ORIGINAL_ORDER_123')

if response.success?
  puts "状态: #{response.status}"          # SUCCESS, FAIL, PROCESSING
  puts "金额: #{response.amount}"
  puts "币种: #{response.currency}"
  puts "卡号: #{response.card_no}"         # 脱敏卡号
  puts "卡组织: #{response.card_orgn}"     # VISA, MASTERCARD 等
end

退款 (Refund)

response = client.refund(
  ori_access_order_id: 'ORIGINAL_ORDER_123',
  refund_amount: '50.00'
)

if response.success?
  puts '退款成功'
else
  puts "退款失败: #{response.result_desc}"
end

验证回调签名

# Rails Controller
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token, only: [:allinpay]

  def allinpay
    client = AllinpayCnp.client

    unless client.verify_callback(params.to_unsafe_h)
      render plain: 'FAIL' and return
    end

    if params[:resultCode] == '0000'
      payment = Payment.find_by(gateway_order_id: params[:accessOrderId])
      payment&.mark_as_paid!(
        transaction_ref: params[:orderId],
        card_no: params[:cardNo],
        card_orgn: params[:cardOrgn]
      )
    end

    render plain: 'SUCCESS'  # 必须返回 SUCCESS
  end
end

多商户模式 (Per-call credentials)

如果你的应用需要为多个商户代发请求 (商户模式 / 非机构模式),每个商户拥有独立的密钥对。可在调用时传入 private_key / public_key / merchant_no,会覆盖全局配置:

response = client.unified_pay(
  access_order_id: "ORDER_#{Time.now.to_i}",
  amount: '100.00',
  currency: 'HKD',
  urls: { notify_url: '...', return_url: '...' },
  merchant_no: merchant.allinpay_id,
  private_key: merchant.allinpay_private_key,
  public_key:  merchant.allinpay_public_key
)

# query / refund 同样支持
client.query(ori_access_order_id: 'ORDER_123',
             merchant_no: merchant.allinpay_id,
             private_key: merchant.allinpay_private_key,
             public_key:  merchant.allinpay_public_key)

# 回调验签时使用对应商户的 public_key
client.verify_callback(params, public_key: merchant.allinpay_public_key)

未传入时会回退到 AllinpayCnp.configure 中的全局配置,因此单商户用法完全向后兼容。

Response 对象

所有 API 方法都返回 Response 对象:

response.success?        # 是否成功 (resultCode == '0000')
response.failure?        # 是否失败
response.result_code     # 返回码
response.result_desc     # 返回描述
response.payment_url     # 支付页面 URL (unified_pay)
response.access_order_id # 商户订单号
response.order_id        # CNP 系统订单号
response.status          # 订单状态 (query)
response.amount          # 金额
response.currency        # 币种
response.card_no         # 脱敏卡号
response.card_orgn       # 卡组织
response.body            # 原始响应 Hash
response['customField']  # 获取任意字段

API 地址

环境 Unified Pay QuickPay
测试 https://cnp-test.allinpay.com/gateway/cnp/unifiedPay https://cnp-test.allinpay.com/gateway/cnp/quickpay
生产 https://cnp.allinpay.com/gateway/cnp/unifiedPay https://cnp.allinpay.com/gateway/cnp/quickpay

运行测试

bundle install
bundle exec rspec

目录结构

lib/
├── allinpay_cnp.rb           # 主入口
└── allinpay_cnp/
    ├── version.rb            # 版本号
    ├── config.rb             # 配置类
    ├── signature.rb          # RSA2 签名
    ├── request.rb            # HTTP 请求
    ├── response.rb           # 响应封装
    └── client.rb             # API 客户端