├── .gitignore ├── src ├── CacertFile │ ├── old_alipay_public_key.pem │ └── wx_cacert.pem ├── Common │ ├── PayException.php │ ├── BaseStrategy.php │ ├── Cmb │ │ └── Data │ │ │ ├── PubKeyData.php │ │ │ ├── BindCardData.php │ │ │ ├── Query │ │ │ ├── ChargeQueryData.php │ │ │ └── RefundQueryData.php │ │ │ ├── RefundData.php │ │ │ ├── CmbBaseData.php │ │ │ └── Charge │ │ │ └── ChargeData.php │ ├── Weixin │ │ ├── Data │ │ │ ├── BackPubChargeData.php │ │ │ ├── BackAppChargeData.php │ │ │ ├── Query │ │ │ │ ├── TransferQueryData.php │ │ │ │ ├── ChargeQueryData.php │ │ │ │ └── RefundQueryData.php │ │ │ ├── WxBaseData.php │ │ │ ├── Charge │ │ │ │ ├── AppChargeData.php │ │ │ │ ├── BarChargeData.php │ │ │ │ ├── WapChargeData.php │ │ │ │ ├── ChargeBaseData.php │ │ │ │ ├── QrChargeData.php │ │ │ │ └── PubChargeData.php │ │ │ └── TransferData.php │ │ └── WechatHelper.php │ ├── Ali │ │ └── Data │ │ │ ├── Query │ │ │ ├── TransferQueryData.php │ │ │ ├── ChargeQueryData.php │ │ │ └── RefundQueryData.php │ │ │ ├── Charge │ │ │ ├── AppChargeData.php │ │ │ ├── QrChargeData.php │ │ │ ├── WapChargeData.php │ │ │ ├── WebChargeData.php │ │ │ ├── ChargeBaseData.php │ │ │ └── BarChargeData.php │ │ │ ├── RefundData.php │ │ │ ├── TransData.php │ │ │ └── AliBaseData.php │ └── ConfigInterface.php ├── Notify │ ├── PayNotifyInterface.php │ └── NotifyStrategy.php ├── Helper │ └── Cmb │ │ ├── BindCardHelper.php │ │ └── PubKeyHelper.php ├── Charge │ ├── Cmb │ │ └── CmbCharge.php │ ├── Wx │ │ ├── WxQrCharge.php │ │ ├── WxWapCharge.php │ │ ├── WxBarCharge.php │ │ ├── WxAppCharge.php │ │ └── WxPubCharge.php │ └── Ali │ │ ├── AliAppCharge.php │ │ ├── AliWapCharge.php │ │ ├── AliQrCharge.php │ │ ├── AliWebCharge.php │ │ └── AliBarCharge.php ├── Utils │ ├── Rc4Encrypt.php │ ├── DataParser.php │ ├── StrUtil.php │ ├── RsaEncrypt.php │ ├── Rsa2Encrypt.php │ └── ArrayUtil.php ├── HelperContext.php ├── Client │ ├── Refund.php │ ├── Helper.php │ ├── Transfer.php │ ├── Query.php │ ├── Charge.php │ └── Notify.php ├── Query │ ├── Cmb │ │ ├── CmbRefundQuery.php │ │ └── CmbChargeQuery.php │ ├── Ali │ │ ├── AliRefundQuery.php │ │ ├── AliTransferQuery.php │ │ └── AliChargeQuery.php │ └── Wx │ │ ├── WxChargeQuery.php │ │ └── WxTransferQuery.php ├── TransferContext.php ├── Refund │ ├── CmbRefund.php │ ├── AliRefund.php │ └── WxRefund.php ├── Trans │ ├── AliTransfer.php │ └── WxTransfer.php ├── RefundContext.php ├── NotifyContext.php ├── Config.php └── QueryContext.php ├── examples ├── index.html ├── cmb │ ├── index.html │ ├── queryPubKey.php │ ├── queryOrder.php │ ├── queryRefund.php │ ├── refund.php │ ├── bindCard.php │ └── charge.php ├── wx │ ├── queryTransfer.php │ ├── queryOrder.php │ ├── queryRefund.php │ ├── transfer.php │ ├── refund.php │ ├── index.html │ ├── appCharge.php │ ├── liteCharge.php │ ├── qrCharge.php │ ├── barCharge.php │ ├── pubCharge.php │ └── wapCharge.php ├── ali │ ├── queryOrder.php │ ├── queryTransfer.php │ ├── queryRefund.php │ ├── refund.php │ ├── transfer.php │ ├── index.html │ ├── appCharge.php │ ├── qrCharge.php │ ├── wapCharge.php │ ├── webCharge.php │ └── barCharge.php ├── testNotify.php ├── cmbconfig.php ├── wxconfig.php ├── notify.php └── aliconfig.php ├── autoload.php ├── composer.json ├── LICENSE ├── SUPPORT.md └── CONTRIBUTING.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | /codecept.phar 4 | /examples/test.php 5 | /examples/wx/pem 6 | /vendor/ 7 | /examples_bak/ 8 | /composer.lock 9 | /examples/wxconfig-bak.php 10 | /examples/wx/pem/ 11 | -------------------------------------------------------------------------------- /src/CacertFile/old_alipay_public_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnxj/9qwVfgoUh/y2W89L6BkRA 3 | FljhNhgPdyPuBV64bfQNN1PjbCzkIM6qRdKBoLPXmKKMiFYnkd6rAoprih3/PrQE 4 | B/VsW8OoM8fxn67UDYuyBTqA23MML9q1+ilIZwBC2AQ2UBVOrFXfFl75p6/B5Ksi 5 | NG9zpgmLCUYuLkxpLQIDAQAB 6 | -----END PUBLIC KEY----- -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 支付演示demo 6 | 7 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /autoload.php: -------------------------------------------------------------------------------- 1 | getMessage(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Notify/PayNotifyInterface.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 招商一网通支付demo 6 | 7 | 8 | 13 | 14 | 18 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /examples/cmb/queryPubKey.php: -------------------------------------------------------------------------------- 1 | errorMessage(); 25 | exit; 26 | } 27 | 28 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); 29 | -------------------------------------------------------------------------------- /examples/wx/queryTransfer.php: -------------------------------------------------------------------------------- 1 | '1489852933', 22 | ]; 23 | 24 | try { 25 | $ret = Query::run(Config::WX_CHARGE, $wxConfig, $data); 26 | } catch (PayException $e) { 27 | echo $e->errorMessage(); 28 | exit; 29 | } 30 | 31 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /src/Common/Cmb/Data/PubKeyData.php: -------------------------------------------------------------------------------- 1 | $this->dateTime, 25 | 'branchNo' => $this->branchNo, 26 | 'merchantNo' => $this->merchantNo, 27 | 'txCode' => CmbConfig::TRADE_CODE, 28 | ]; 29 | 30 | // 这里不能进行过滤空值,招商的空值也要加入签名中 31 | return $reqData; 32 | } 33 | } -------------------------------------------------------------------------------- /examples/wx/queryOrder.php: -------------------------------------------------------------------------------- 1 | '14935505602169', 22 | 'transaction_id' => '20170430190922203640695', 23 | ]; 24 | 25 | try { 26 | $ret = Query::run(Config::WX_CHARGE, $wxConfig, $data); 27 | } catch (PayException $e) { 28 | echo $e->errorMessage(); 29 | exit; 30 | } 31 | 32 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /examples/cmb/queryOrder.php: -------------------------------------------------------------------------------- 1 | '9336161758', 20 | 'date' => '20170428', 21 | 'transaction_id' => '17242823500000000010', 22 | ]; 23 | 24 | try { 25 | $ret = Query::run(Config::CMB_CHARGE, $cmbConfig, $data); 26 | } catch (PayException $e) { 27 | echo $e->errorMessage(); 28 | exit; 29 | } 30 | 31 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /src/Helper/Cmb/BindCardHelper.php: -------------------------------------------------------------------------------- 1 | config->getewayUrl = 'https://mobile.cmbchina.com/mobilehtml/DebitCard/M_NetPay/OneNetRegister/NP_BindCard.aspx'; 19 | if ($this->config->useSandbox) {// 测试 20 | $this->config->getewayUrl = 'http://121.15.180.66:801/mobilehtml/DebitCard/M_NetPay/OneNetRegister/NP_BindCard.aspx'; 21 | } 22 | 23 | return BindCardData::class; 24 | } 25 | } -------------------------------------------------------------------------------- /examples/ali/queryOrder.php: -------------------------------------------------------------------------------- 1 | '15043337047336', 22 | //'trade_no' => '2017090221001004350200242476', 23 | ]; 24 | 25 | try { 26 | $ret = Query::run(Config::ALI_CHARGE, $aliConfig, $data); 27 | } catch (PayException $e) { 28 | echo $e->errorMessage(); 29 | exit; 30 | } 31 | 32 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /examples/ali/queryTransfer.php: -------------------------------------------------------------------------------- 1 | '15043431341', 22 | 'transaction_id' => '201709021100700015026800000163021', 23 | ]; 24 | 25 | try { 26 | $ret = Query::run(Config::ALI_TRANSFER, $aliConfig, $data); 27 | } catch (PayException $e) { 28 | echo $e->errorMessage(); 29 | exit; 30 | } 31 | 32 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /examples/ali/queryRefund.php: -------------------------------------------------------------------------------- 1 | '15043296209218', 21 | 'trade_no' => '2017090221001004350200242476', 22 | 'refund_no' => '15043420895504', 23 | ]; 24 | 25 | try { 26 | $ret = Query::run(Config::ALI_REFUND, $aliConfig, $data); 27 | } catch (PayException $e) { 28 | echo $e->errorMessage(); 29 | exit; 30 | } 31 | 32 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /examples/cmb/queryRefund.php: -------------------------------------------------------------------------------- 1 | '9354737499', 20 | 'refund_no' => '',// 商户退款流水号,长度不超过20位 21 | 'date' => '20170430', 22 | 'refund_id' => '',// 银行退款流水号,长度不超过20位 23 | ]; 24 | 25 | try { 26 | $ret = Query::run(Config::CMB_REFUND, $cmbConfig, $data); 27 | } catch (PayException $e) { 28 | echo $e->errorMessage(); 29 | exit; 30 | } 31 | 32 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /src/Charge/Cmb/CmbCharge.php: -------------------------------------------------------------------------------- 1 | config->getewayUrl = 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?MB_EUserPay'; 23 | if ($this->config->useSandbox) {// 测试 24 | $this->config->getewayUrl = 'http://121.15.180.66:801/NetPayment/BaseHttp.dll?MB_EUserPay'; 25 | } 26 | 27 | return ChargeData::class; 28 | } 29 | } -------------------------------------------------------------------------------- /examples/testNotify.php: -------------------------------------------------------------------------------- 1 | '14935385689468', 22 | 'refund_no' => '14935506214648', 23 | 'transaction_id' => '12345678920170430191024123337865', 24 | 'refund_id' => '1234567892017043019102412333', 25 | ]; 26 | 27 | try { 28 | $ret = Query::run(Config::WX_REFUND, $wxConfig, $data); 29 | } catch (PayException $e) { 30 | echo $e->errorMessage(); 31 | exit; 32 | } 33 | 34 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /examples/ali/refund.php: -------------------------------------------------------------------------------- 1 | '15043296209218', 23 | 'trade_no' => '',// 支付宝交易号, 与 out_trade_no 必须二选一 24 | 'refund_fee' => '0.01', 25 | 'reason' => '我要退款', 26 | 'refund_no' => $refundNo, 27 | ]; 28 | 29 | try { 30 | $ret = Refund::run(Config::ALI_REFUND, $aliConfig, $data); 31 | } catch (PayException $e) { 32 | echo $e->errorMessage(); 33 | exit; 34 | } 35 | 36 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /examples/cmb/refund.php: -------------------------------------------------------------------------------- 1 | '9354737499', 22 | 'date' => '20170430', 23 | 'refund_no' => $refundNo, 24 | 'refund_fee' => 0.01, 25 | 'reason' => '测试帐号退款', 26 | 'operator_id' => '9999', 27 | ]; 28 | 29 | try { 30 | $ret = Refund::run(Config::CMB_REFUND, $cmbConfig, $data); 31 | } catch (PayException $e) { 32 | echo $e->errorMessage(); 33 | exit; 34 | } 35 | 36 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "riverslei/payment", 3 | "type": "library", 4 | "description": "支付宝支付、微信支付、招商一网通支付php SDK。方便快速接入,最完整的开源支付 php sdk", 5 | "keywords": ["alipay", "weixin", "支付宝支付", "微信支付", "集成支付接口SDK", "招商一网通", "一网通"], 6 | "homepage": "https://helei112g.github.io/categories/payment-3/", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "helei", 11 | "email": "dayugog@gmail.com", 12 | "homepage": "https://helei112g.github.io/categories/payment-3/" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=5.6", 17 | "ext-bcmath": "*", 18 | "ext-mbstring": "*", 19 | "guzzlehttp/guzzle": "~6.0" 20 | }, 21 | "require-dev": { 22 | "endroid/qrcode": "~1.9", 23 | "codeception/codeception": "*" 24 | }, 25 | "autoload": { 26 | "psr-4": {"Payment\\": "src/"} 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/ali/transfer.php: -------------------------------------------------------------------------------- 1 | time(), 21 | 'payee_type' => 'ALIPAY_LOGONID', 22 | 'payee_account' => 'aaqlmq0729@sandbox.com',// ALIPAY_USERID: 2088102169940354 ALIPAY_LOGONID:aaqlmq0729@sandbox.com 23 | 'amount' => '1000', 24 | 'remark' => '转账拉,有钱了', 25 | 'payer_show_name' => '一个未来的富豪', 26 | ]; 27 | 28 | try { 29 | $ret = Transfer::run(Config::ALI_TRANSFER, $aliConfig, $data); 30 | } catch (PayException $e) { 31 | echo $e->errorMessage(); 32 | exit; 33 | } 34 | 35 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /examples/wx/transfer.php: -------------------------------------------------------------------------------- 1 | time(), 20 | 'openid' => 'o-e_mwTXTaxEhBM8xDoj1ui1f950', 21 | 'check_name' => 'NO_CHECK',// NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名 OPTION_CHECK:针对已实名认证的用户才校验真实姓名 22 | 'payer_real_name' => '何磊', 23 | 'amount' => '1', 24 | 'desc' => '测试转账', 25 | 'spbill_create_ip' => '127.0.0.1', 26 | ]; 27 | 28 | try { 29 | $ret = Transfer::run(Config::WX_TRANSFER, $wxConfig, $data); 30 | } catch (PayException $e) { 31 | echo $e->errorMessage(); 32 | exit; 33 | } 34 | 35 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /src/Common/Weixin/Data/BackPubChargeData.php: -------------------------------------------------------------------------------- 1 | retData = [ 26 | 'appId' => $this->appId, 27 | 'timeStamp' => time() . '', 28 | 'nonceStr' => $this->nonceStr, 29 | 'package' => 'prepay_id=' . $this->prepay_id, 30 | 'signType' => 'MD5',// 签名算法,暂支持MD5 31 | ]; 32 | } 33 | 34 | protected function checkDataParam() 35 | { 36 | // 不进行检查 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/wx/refund.php: -------------------------------------------------------------------------------- 1 | '14935385689468', 23 | 'total_fee' => '3.01', 24 | 'refund_fee' => '3.01', 25 | 'refund_no' => $refundNo, 26 | 'refund_account' => WxConfig::REFUND_RECHARGE,// REFUND_RECHARGE:可用余额退款 REFUND_UNSETTLED:未结算资金退款(默认) 27 | ]; 28 | 29 | try { 30 | $ret = Refund::run(Config::WX_REFUND, $wxConfig, $data); 31 | } catch (PayException $e) { 32 | echo $e->errorMessage(); 33 | exit; 34 | } 35 | 36 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /src/Charge/Wx/WxQrCharge.php: -------------------------------------------------------------------------------- 1 | config->tradeType = 'NATIVE';// 微信文档这里写错了 19 | return QrChargeData::class; 20 | } 21 | 22 | /** 23 | * 处理扫码支付的返回值 24 | * @param array $ret 25 | * @return string 可生产二维码的uri 26 | * @author helei 27 | */ 28 | protected function retData(array $ret) 29 | { 30 | if ($this->config->returnRaw) { 31 | return $ret; 32 | } 33 | 34 | // 扫码支付,返回链接 35 | return $ret['code_url']; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/cmbconfig.php: -------------------------------------------------------------------------------- 1 | true,// 是否使用 招商测试系统 12 | 13 | 'branch_no' => 'xxx', // 商户分行号,4位数字 14 | 'merchant_no' => 'xxxx',// 商户号,6位数字 15 | 'mer_key' => 'xxxxxx',// 秘钥16位,包含大小写字母 数字 16 | 17 | // 招商的公钥,建议每天凌晨2:15发起查询招行公钥请求更新公钥。 18 | 'cmb_pub_key' => 'xxxxx', 19 | 20 | 'op_pwd' => 'xxxxx',// 操作员登录密码。 21 | 'sign_type' => 'SHA-256',// 签名算法,固定为“SHA-256” 22 | 'limit_pay' => [ 23 | //'A', 24 | ],// 允许支付的卡类型,默认对支付卡种不做限制,储蓄卡和信用卡均可支付 A:储蓄卡支付,即禁止信用卡支付 25 | 26 | 'notify_url' => 'http://114.215.86.31/__readme/phpinfo.php',// 支付成功的回调 27 | 28 | 'sign_notify_url' => 'http://114.215.86.31/__readme/phpinfo.php',// 成功签约结果通知地址 29 | 30 | 'return_url' => 'https://helei112g.github.io/',// 如果是h5支付,可以设置该值,返回到指定页面 31 | 32 | 'return_raw' => false,// 在处理回调时,是否直接返回原始数据,默认为true 33 | ]; -------------------------------------------------------------------------------- /src/Common/Weixin/Data/BackAppChargeData.php: -------------------------------------------------------------------------------- 1 | retData = [ 25 | 'appid' => $this->appId, 26 | 'partnerid' => $this->mchId, 27 | 'prepayid' => $this->prepay_id, 28 | 'package' => 'Sign=WXPay', 29 | 'noncestr' => StrUtil::getNonceStr(), 30 | 'timestamp' => time(), 31 | ]; 32 | } 33 | 34 | protected function checkDataParam() 35 | { 36 | // 对于返回数据不做检查检查 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/wx/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 微信支付demo 6 | 7 | 8 | 16 | 17 | 22 | 23 | 27 | 28 | -------------------------------------------------------------------------------- /src/Common/Ali/Data/Query/TransferQueryData.php: -------------------------------------------------------------------------------- 1 | $this->trans_no, 23 | 'order_id' => $this->transaction_id, 24 | ]; 25 | 26 | return $content; 27 | } 28 | 29 | protected function checkDataParam() 30 | { 31 | $transNo = $this->trans_no; 32 | $transactionId = $this->transaction_id; 33 | 34 | // 二者不能同时为空 35 | if (empty($transactionId) && empty($transNo)) { 36 | throw new PayException('必须提供支付宝转账单据号或者商户转账单号'); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /examples/wx/appCharge.php: -------------------------------------------------------------------------------- 1 | 'test body', 23 | 'subject' => 'test subject', 24 | 'order_no' => $orderNo, 25 | 'timeout_express' => time() + 600,// 表示必须 600s 内付款 26 | 'amount' => '3.01',// 微信沙箱模式,需要金额固定为3.01 27 | 'return_param' => '123', 28 | 'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址 29 | ]; 30 | 31 | try { 32 | $ret = Charge::run(Config::WX_CHANNEL_APP, $wxConfig, $payData); 33 | } catch (PayException $e) { 34 | echo $e->errorMessage(); 35 | exit; 36 | } 37 | 38 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /src/Charge/Wx/WxWapCharge.php: -------------------------------------------------------------------------------- 1 | config->tradeType = 'MWEB'; 20 | return WapChargeData::class; 21 | } 22 | 23 | /** 24 | * 这里由于 25 | * @param array $ret 26 | * @return mixed 27 | */ 28 | protected function retData(array $ret) 29 | { 30 | if ($this->config->returnRaw) { 31 | return $ret; 32 | } 33 | 34 | $wabUrl = $ret['mweb_url']; 35 | if ($this->config->returnUrl) { 36 | $wabUrl .= '&redirect_url=' . urlencode($this->config->returnUrl); 37 | } 38 | 39 | return $wabUrl; 40 | } 41 | } -------------------------------------------------------------------------------- /examples/ali/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 支付宝支付demo 6 | 7 | 8 | 15 | 16 | 21 | 22 | 26 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /examples/wxconfig.php: -------------------------------------------------------------------------------- 1 | true,// 是否使用 微信支付仿真测试系统 10 | 11 | 'app_id' => 'wxxxxxx', // 公众账号ID 12 | 'mch_id' => 'xxxxx',// 商户id 13 | 'md5_key' => 'xxxxxxx',// md5 秘钥 14 | 'app_cert_pem' => dirname(__FILE__) . DIRECTORY_SEPARATOR . 'wx' . DIRECTORY_SEPARATOR . 'pem' . DIRECTORY_SEPARATOR . 'weixin_app_cert.pem', 15 | 'app_key_pem' => dirname(__FILE__) . DIRECTORY_SEPARATOR . 'wx' . DIRECTORY_SEPARATOR . 'pem' . DIRECTORY_SEPARATOR . 'weixin_app_key.pem', 16 | 'sign_type' => 'MD5',// MD5 HMAC-SHA256 17 | 'limit_pay' => [ 18 | //'no_credit', 19 | ],// 指定不能使用信用卡支付 不传入,则均可使用 20 | 'fee_type' => 'CNY',// 货币类型 当前仅支持该字段 21 | 22 | 'notify_url' => 'https://helei112g.github.io/v1/notify/wx', 23 | 24 | 'redirect_url' => 'https://helei112g.github.io/',// 如果是h5支付,可以设置该值,返回到指定页面 25 | 26 | 'return_raw' => false,// 在处理回调时,是否直接返回原始数据,默认为true 27 | ]; -------------------------------------------------------------------------------- /src/Charge/Wx/WxBarCharge.php: -------------------------------------------------------------------------------- 1 | config->returnRaw) { 35 | return $ret; 36 | } 37 | 38 | return $ret; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2017 Daniele Alessandri 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/Charge/Ali/AliAppCharge.php: -------------------------------------------------------------------------------- 1 | config->method = $this->method; 27 | // 以下两种方式任选一种 28 | return AppChargeData::class; 29 | } 30 | 31 | /** 32 | * 组装返回的数据格式 33 | * @param array $data 34 | * @return string 35 | */ 36 | protected function retData(array $data) 37 | { 38 | $data = parent::retData($data); 39 | 40 | // 组装成 key=value&key=value 形式返回 41 | return http_build_query($data); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Charge/Ali/AliWapCharge.php: -------------------------------------------------------------------------------- 1 | config->method = $this->method; 27 | // 以下两种方式任选一种 28 | return WapChargeData::class; 29 | } 30 | 31 | /** 32 | * 返回可发起h5支付的请求 33 | * @param array $data 34 | * @return array|string 35 | */ 36 | protected function retData(array $data) 37 | { 38 | $data = parent::retData($data); 39 | 40 | return $this->config->getewayUrl . '?' . http_build_query($data); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/ali/appCharge.php: -------------------------------------------------------------------------------- 1 | 'ali qr pay', 23 | 'subject' => '测试支付宝扫码支付', 24 | 'order_no' => $orderNo, 25 | 'timeout_express' => time() + 600,// 表示必须 600s 内付款 26 | 'amount' => '0.01',// 单位为元 ,最小为0.01 27 | 'return_param' => '123123', 28 | // 'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址 29 | 'goods_type' => '1',// 0—虚拟类商品,1—实物类商品 30 | 'store_id' => '', 31 | ]; 32 | 33 | try { 34 | $str = Charge::run(Config::ALI_CHANNEL_APP, $aliConfig, $payData); 35 | } catch (PayException $e) { 36 | echo $e->errorMessage(); 37 | exit; 38 | } 39 | 40 | echo $str;// 这里如果直接输出到页面,¬ 会被转义,请注意 -------------------------------------------------------------------------------- /examples/wx/liteCharge.php: -------------------------------------------------------------------------------- 1 | 'test body', 25 | 'subject' => 'test subject', 26 | 'order_no' => $orderNo, 27 | 'timeout_express' => time() + 600,// 表示必须 600s 内付款 28 | 'amount' => '3.01',// 微信沙箱模式,需要金额固定为3.01 29 | 'return_param' => '123', 30 | 'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址 31 | 'openid' => 'ottkCuO1PW1Dnh6PWFffNk-2MPbY', 32 | 'product_id' => '123', 33 | ]; 34 | 35 | try { 36 | $ret = Charge::run(Config::WX_CHANNEL_LITE, $wxConfig, $payData); 37 | } catch (PayException $e) { 38 | echo $e->errorMessage(); 39 | exit; 40 | } 41 | 42 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /src/CacertFile/wx_cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV 3 | UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy 4 | dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 5 | MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx 6 | dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B 7 | AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f 8 | BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A 9 | cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC 10 | AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ 11 | MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm 12 | aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw 13 | ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj 14 | IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF 15 | MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA 16 | A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y 17 | 7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh 18 | 1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 19 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /examples/ali/qrCharge.php: -------------------------------------------------------------------------------- 1 | 'ali qr pay', 23 | 'subject' => '测试支付宝扫码支付', 24 | 'order_no' => $orderNo, 25 | 'timeout_express' => time() + 600,// 表示必须 600s 内付款 26 | 'amount' => '0.01',// 单位为元 ,最小为0.01 27 | 'return_param' => '123123', 28 | // 'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址 29 | 'goods_type' => '1',// 0—虚拟类商品,1—实物类商品 30 | 'store_id' => '', 31 | 32 | 'operator_id' => '', 33 | 'terminal_id' => '',// 终端设备号(门店号或收银设备ID) 默认值 web 34 | ]; 35 | 36 | 37 | try { 38 | $url = Charge::run(Config::ALI_CHANNEL_QR, $aliConfig, $payData); 39 | } catch (PayException $e) { 40 | echo $e->errorMessage(); 41 | exit; 42 | } 43 | 44 | echo $url; 45 | -------------------------------------------------------------------------------- /examples/ali/wapCharge.php: -------------------------------------------------------------------------------- 1 | 'ali wap pay', 23 | 'subject' => '测试支付宝手机网站支付', 24 | 'order_no' => $orderNo, 25 | 'timeout_express' => time() + 600,// 表示必须 600s 内付款 26 | 'amount' => '0.01',// 单位为元 ,最小为0.01 27 | 'return_param' => 'tata',// 一定不要传入汉字,只能是 字母 数字组合 28 | // 'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址 29 | 'goods_type' => '1',// 0—虚拟类商品,1—实物类商品 30 | 'store_id' => '', 31 | 'quit_url' => 'http://helei112g.github.io', // 收银台的返回按钮(用户打断支付操作时返回的地址,4.0.3版本新增) 32 | ]; 33 | 34 | try { 35 | $url = Charge::run(Config::ALI_CHANNEL_WAP, $aliConfig, $payData); 36 | } catch (PayException $e) { 37 | echo $e->errorMessage(); 38 | exit; 39 | } 40 | 41 | header('Location:' . $url); -------------------------------------------------------------------------------- /examples/wx/qrCharge.php: -------------------------------------------------------------------------------- 1 | 'test body', 24 | 'subject' => 'test subject', 25 | 'order_no' => $orderNo, 26 | 'timeout_express' => time() + 600,// 表示必须 600s 内付款 27 | 'amount' => '3.01',// 微信沙箱模式,需要金额固定为3.01 28 | 'return_param' => '123', 29 | 'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址 30 | 'openid' => 'ohQeiwnNrAsfdsdf9VvmGFIhba--k', 31 | 'product_id' => '123', 32 | 33 | // 如果是服务商,请提供以下参数 34 | 'sub_appid' => '',//微信分配的子商户公众账号ID 35 | 'sub_mch_id' => '',// 微信支付分配的子商户号 36 | ]; 37 | 38 | try { 39 | $ret = Charge::run(Config::WX_CHANNEL_QR, $wxConfig, $payData); 40 | } catch (PayException $e) { 41 | echo $e->errorMessage(); 42 | exit; 43 | } 44 | 45 | echo $ret; -------------------------------------------------------------------------------- /src/Common/Weixin/Data/Query/TransferQueryData.php: -------------------------------------------------------------------------------- 1 | retData = [ 30 | 'appid' => $this->appId, 31 | 'mch_id' => $this->mchId, 32 | 'nonce_str' => $this->nonceStr, 33 | //'sign_type' => $this->signType,// 转账查询,不能加入该数据 34 | 35 | 'partner_trade_no' => $this->trans_no, 36 | ]; 37 | 38 | $this->retData = ArrayUtil::paraFilter($this->retData); 39 | } 40 | 41 | protected function checkDataParam() 42 | { 43 | $transNo = $this->trans_no; 44 | if (empty($transNo)) { 45 | throw new PayException('请提供商户调用企业付款API时使用的商户订单号'); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/Charge/Ali/AliQrCharge.php: -------------------------------------------------------------------------------- 1 | config->method = $this->method; 25 | return QrChargeData::class; 26 | } 27 | 28 | /** 29 | * 处理扫码支付的返回值 30 | * @param array $ret 31 | * 32 | * @throws PayException 33 | * @return string 可生产二维码的uri 34 | * @author helei 35 | */ 36 | protected function retData(array $ret) 37 | { 38 | $url = parent::retData($ret); 39 | 40 | // 发起网络请求 41 | try { 42 | $data = $this->sendReq($url); 43 | } catch (PayException $e) { 44 | throw $e; 45 | } 46 | 47 | return $data['qr_code']; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Charge/Ali/AliWebCharge.php: -------------------------------------------------------------------------------- 1 | config->method = $this->method; 30 | // 以下两种方式均可以 31 | return WebChargeData::class; 32 | //return 'Payment\Common\Ali\Data\Charge\WebChargeData'; 33 | } 34 | 35 | /** 36 | * 返回可发起h5支付的请求 37 | * @param array $data 38 | * @return array|string 39 | */ 40 | protected function retData(array $data) 41 | { 42 | $data = parent::retData($data); 43 | 44 | return $this->config->getewayUrl . '?' . http_build_query($data); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/ali/webCharge.php: -------------------------------------------------------------------------------- 1 | 'ali web pay', 24 | 'subject' => '测试支付宝电脑网站支付', 25 | 'order_no' => $orderNo, 26 | 'timeout_express' => time() + 600,// 表示必须 600s 内付款 27 | 'amount' => '0.01',// 单位为元 ,最小为0.01 28 | 'return_param' => '123123', 29 | // 'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址 30 | 'goods_type' => '1',// 0—虚拟类商品,1—实物类商品 31 | 'store_id' => '', 32 | 33 | // 说明地址:https://doc.open.alipay.com/doc2/detail.htm?treeId=270&articleId=105901&docType=1 34 | // 建议什么也不填 35 | 'qr_mod' => '', 36 | ]; 37 | 38 | try { 39 | $url = Charge::run(Config::ALI_CHANNEL_WEB, $aliConfig, $payData); 40 | } catch (PayException $e) { 41 | echo $e->errorMessage(); 42 | exit; 43 | } 44 | 45 | header('Location:' . $url); -------------------------------------------------------------------------------- /examples/wx/barCharge.php: -------------------------------------------------------------------------------- 1 | 'test body', 24 | 'subject' => 'test subject', 25 | 'order_no' => $orderNo, 26 | 'timeout_express' => time() + 600,// 表示必须 600s 内付款 27 | 'amount' => '0.01',// 微信沙箱模式,需要金额固定为0.01 28 | 'return_param' => '123', 29 | 'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址 30 | 'terminal_id' => 'web',// 终端设备号(门店号或收银设备ID) 默认值 web 31 | 'auth_code' => '1231212232323123123', 32 | 33 | // 如果是服务商,请提供以下参数 34 | 'sub_appid' => '',//微信分配的子商户公众账号ID 35 | 'sub_mch_id' => '',// 微信支付分配的子商户号 36 | ]; 37 | 38 | try { 39 | $ret = Charge::run(Config::WX_CHANNEL_BAR, $wxConfig, $payData); 40 | } catch (PayException $e) { 41 | echo $e->errorMessage(); 42 | exit; 43 | } 44 | 45 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /src/Common/ConfigInterface.php: -------------------------------------------------------------------------------- 1 | initConfig($config); 45 | } catch (PayException $e) { 46 | throw $e; 47 | } 48 | } 49 | 50 | /** 51 | * 配置文件初始化具体实现 52 | * @param array $config 53 | */ 54 | abstract protected function initConfig(array $config); 55 | } 56 | -------------------------------------------------------------------------------- /examples/ali/barCharge.php: -------------------------------------------------------------------------------- 1 | 'ali bar pay', 23 | 'subject' => '测试支付宝条码支付', 24 | 'order_no' => $orderNo, 25 | 'timeout_express' => time() + 600,// 表示必须 600s 内付款 26 | 'amount' => '0.01',// 单位为元 ,最小为0.01 27 | 'return_param' => '123123', 28 | // 'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址 29 | 'goods_type' => '1',// 0—虚拟类商品,1—实物类商品 30 | 'store_id' => '', 31 | 32 | 'operator_id' => '', 33 | 'terminal_id' => '',// 终端设备号(门店号或收银设备ID) 默认值 web 34 | 'scene' => 'bar_code',// 条码支付:bar_code 声波支付:wave_code 35 | 'auth_code' => '12312313123', 36 | ]; 37 | 38 | try { 39 | $ret = Charge::run(Config::ALI_CHANNEL_BAR, $aliConfig, $payData); 40 | } catch (PayException $e) { 41 | echo $e->errorMessage(); 42 | exit; 43 | } 44 | 45 | var_dump($ret); -------------------------------------------------------------------------------- /examples/wx/pubCharge.php: -------------------------------------------------------------------------------- 1 | 'test body', 25 | 'subject' => 'test subject', 26 | 'order_no' => $orderNo, 27 | 'timeout_express' => time() + 600,// 表示必须 600s 内付款 28 | 'amount' => '3.01',// 微信沙箱模式,需要金额固定为3.01 29 | 'return_param' => '123', 30 | 'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址 31 | 'openid' => 'ottkCuO1PW1Dnh6PWFffNk-2MPbY', 32 | 'product_id' => '123', 33 | 34 | // 如果是服务商,请提供以下参数 35 | 'sub_appid' => '',//微信分配的子商户公众账号ID 36 | 'sub_mch_id' => '',// 微信支付分配的子商户号 37 | 'sub_openid' => '',// 用户在子商户appid下的唯一标识 38 | ]; 39 | 40 | try { 41 | $ret = Charge::run(Config::WX_CHANNEL_PUB, $wxConfig, $payData); 42 | } catch (PayException $e) { 43 | echo $e->errorMessage(); 44 | exit; 45 | } 46 | 47 | echo json_encode($ret, JSON_UNESCAPED_UNICODE); -------------------------------------------------------------------------------- /examples/wx/wapCharge.php: -------------------------------------------------------------------------------- 1 | 'test body', 24 | 'subject' => 'test subject', 25 | 'order_no' => $orderNo, 26 | 'timeout_express' => time() + 600,// 表示必须 600s 内付款 27 | 'amount' => '3.01',// 微信沙箱模式,需要金额固定为3.01 28 | 'return_param' => '123', 29 | 'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址 30 | 31 | //{"h5_info": {"type":"Wap","wap_url": "https://pay.qq.com","wap_name": "腾讯充值"}} 32 | 'scene_info' => [ 33 | 'type' => 'Wap',// IOS Android Wap 腾讯建议 IOS ANDROID 采用app支付 34 | 'wap_url' => 'https://helei112g.github.io/',//自己的 wap 地址 35 | 'wap_name' => '测试充值', 36 | ], 37 | ]; 38 | 39 | try { 40 | $url = Charge::run(Config::WX_CHANNEL_WAP, $wxConfig, $payData); 41 | } catch (PayException $e) { 42 | echo $e->errorMessage(); 43 | exit; 44 | } 45 | 46 | echo $url; -------------------------------------------------------------------------------- /src/Common/Ali/Data/Query/ChargeQueryData.php: -------------------------------------------------------------------------------- 1 | $this->out_trade_no, 30 | 'trade_no' => $this->trade_no, 31 | ]; 32 | 33 | return $content; 34 | } 35 | 36 | /** 37 | * 检查参数 38 | * @author helei 39 | */ 40 | protected function checkDataParam() 41 | { 42 | $tradeNo = $this->trade_no;// 支付宝交易号,查询效率高 43 | $outTradeNo = $this->out_trade_no;// 商户订单号,查询效率低,不建议使用 44 | 45 | // 二者不能同时为空 46 | if (empty($outTradeNo) && empty($tradeNo)) { 47 | throw new PayException('必须提供支付宝交易号或者商户网站唯一订单号。建议使用支付宝交易号'); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Common/Ali/Data/Query/RefundQueryData.php: -------------------------------------------------------------------------------- 1 | $this->out_trade_no, 24 | 'trade_no' => $this->trade_no, 25 | 'out_request_no' => $this->refund_no, 26 | ]; 27 | 28 | return $content; 29 | } 30 | 31 | protected function checkDataParam() 32 | { 33 | $tradeNo = $this->trade_no;// 支付宝交易号,查询效率高 34 | $outTradeNo = $this->out_trade_no;// 商户订单号,查询效率低,不建议使用 35 | 36 | // 二者不能同时为空 37 | if (empty($outTradeNo) && empty($tradeNo)) { 38 | throw new PayException('必须提供支付宝交易号或者商户网站唯一订单号。建议使用支付宝交易号'); 39 | } 40 | 41 | $refundNo = $this->refund_no; 42 | if (empty($refundNo)) { 43 | throw new PayException('支付宝查询退款,必须传入提款的请求号。如果在退款请求时未传入,则该值为创建交易时的外部交易号'); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/Charge/Wx/WxAppCharge.php: -------------------------------------------------------------------------------- 1 | config->tradeType = 'APP'; 20 | return AppChargeData::class; 21 | } 22 | 23 | /** 24 | * 处理APP支付的返回值。直接返回与微信文档对应的字段 25 | * @param array $ret 26 | * 27 | * @return array $data 28 | * 29 | * ```php 30 | * $data = [ 31 | * 'appid' => '', // 应用ID 32 | * 'partnerid' => '', // 商户号 33 | * 'prepayid' => '', // 预支付交易会话ID 34 | * 'package' => '', // 扩展字段 固定值:Sign=WXPay 35 | * 'noncestr' => '', // 随机字符串 36 | * 'timestamp' => '', // 时间戳 37 | * 'sign' => '', // 签名 38 | * ]; 39 | * ``` 40 | * @author helei 41 | */ 42 | protected function retData(array $ret) 43 | { 44 | $back = new BackAppChargeData($this->config, $ret); 45 | 46 | $back->setSign(); 47 | $backData = $back->getData(); 48 | 49 | return $backData; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | **打赏名单** 2 | 3 | 名字 | 金额 | 留言 | 时间 4 | ---|---|---|--- 5 | 未留名 | 20.00 | 感谢无私 | 2017-09-2 6 | 未留名 | 8.88 | 感谢 | 2017-09-19 7 | ** | 16.80 | 严重支持开源(我想问为什么是严重?) | 2017-08-22 8 | *滔 | 16.80 | 谢谢帮助 | 2017-08-09 9 | 鑫* | 10.00 | 来自支付宝 | 2017-08-01 10 | 来自微信(付款) | 10.00 | 加油,很棒! | 2017-07-25 11 | 一掠而过 | 10.00 | 一点心意 | 2017-07-14 12 | 北国之雪 | 30.00 | Payment好用 | 2017-05-04 13 | 里暮色中 | 50.00 | Payment越来越好(感谢发现一个bug issue#39) | 2017-05-01 14 | lobtao | 8.88 | 你的内裤非常好用 | 2017-04-26 15 | 182xxxx | 10.00 | 支持 | 2017-04-19 16 | 来自简单 | 200.00 | 支付宝服务商模式咨询 | 2017-04-16 17 | 刘华 | 50.00 | 微信支付咨询 | 2017-04-14 18 | 靖 | 88.00 | 感谢开源 | 2017-03-19 19 | 李 | 150.00 | 打赏支持 | 2017-03-14 20 | Alex.Ma | 6.66 | 支持 | 2017-03-13 21 | 阿笨 | 10.00 | 打算使用,先感谢一下 | 2017-03-10 22 | 彦 | 88.00 | 感觉还不错,特打赏88元,略表感谢。 | 2017-02-28 23 | 汤明洋 | 66.66 | 支持一下 | 2017-02-19 24 | 李仕建同学 | 18.88 | 新春快乐 | 2017-02-09 25 | 凡额 | 50.00 | 帮助调试,谢谢了 | 2017-01-18 26 | Thans秦 | 66.66 | 商业使用 | 2017-01-08 27 | John | 10.00 | 设计很棒 | 2017-01-06 28 | Davidw | 699.00 | 支持开发2.0 | 2016-12-15 29 | 宁静致远 | 10.00 | 鼓励你,加油额 | 2016-12-13 30 | k7 | 8.00 | 批量付款,一次成功 | 2016-11-24 31 | 洋 | 50.00 | 资助开源 | 2016-11-23 32 | 张仲东 | 50.00 | 接口封装的不错 | 2016-11-17 33 | 放下...快乐 | 1000.00 | 支付宝即时到帐处理 | 2016-11-15 34 | Robin Core Animation | 50.00 | 解决微信支付问题 | 2016-11-04 35 | 5Z4 | 20.00 | 解决回调问题 | 2016-10-31 36 | 哈罗Joe | 1.00 | 加油~~ | 2016-8-23 37 | 小兵~招UI前端 | 50.00 | 继续努力,喝杯水吧:-) | 2016-8-14 38 | 尊称韦爵爷 | 1.00 | 赶紧出个yii的扩展 | 2016-7-22 39 | 一米市集 | 1000.00 | 希望提供技术长期合作 | 2016-7-20 40 | 张松 | 15.00 | 不错,已用到项目中 | 2016-6-17 -------------------------------------------------------------------------------- /src/Helper/Cmb/PubKeyHelper.php: -------------------------------------------------------------------------------- 1 | config->getewayUrl = 'https://b2b.cmbchina.com/CmbBank_B2B/UI/NetPay/DoBusiness.ashx'; 20 | if ($this->config->useSandbox) {// 测试 21 | $this->config->getewayUrl = 'http://121.15.180.72/CmbBank_B2B/UI/NetPay/DoBusiness.ashx'; 22 | } 23 | 24 | return PubKeyData::class; 25 | } 26 | 27 | protected function retData(array $ret) 28 | { 29 | $json = json_encode($ret, JSON_UNESCAPED_UNICODE); 30 | 31 | $postData = CmbConfig::REQ_FILED_NAME . '=' . $json; 32 | $retData = $this->sendReq($postData); 33 | 34 | if ($this->config->returnRaw) { 35 | $retData['channel'] = Config::CMB_PUB_KEY; 36 | return $retData; 37 | } 38 | 39 | // 正确情况 40 | $rData = [ 41 | 'is_success' => 'T', 42 | 'response' => [ 43 | 'pub_key' => $retData['fbPubKey'], 44 | 'channel' => Config::CMB_PUB_KEY, 45 | 'time' => date('Y-m-d H:i:s', strtotime($retData['dateTime'])),// Y-m-d H:i:s, 46 | ], 47 | ]; 48 | 49 | return $rData; 50 | } 51 | } -------------------------------------------------------------------------------- /src/Charge/Wx/WxPubCharge.php: -------------------------------------------------------------------------------- 1 | config->tradeType = 'JSAPI'; 20 | return PubChargeData::class; 21 | } 22 | 23 | /** 24 | * 处理公众号支付的返回值。直接返回与微信文档对应的字段 25 | * @param array $ret 26 | * 27 | * @return string $data 包含以下键 28 | * 29 | * ```php 30 | * $data = [ 31 | * 'appId' => '', // 公众号id 32 | * 'package' => '', // 订单详情扩展字符串 统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=*** 33 | * 'nonceStr' => '', // 随机字符串 34 | * 'timeStamp' => '', // 时间戳 35 | * 'signType' => '', // 签名算法,暂支持MD5 36 | * 'paySign' => '', // 签名 37 | * ]; 38 | * ``` 39 | * @author helei 40 | */ 41 | protected function retData(array $ret) 42 | { 43 | $back = new BackPubChargeData($this->config, $ret); 44 | 45 | $back->setSign(); 46 | $backData = $back->getData(); 47 | 48 | $backData['paySign'] = $backData['sign']; 49 | // 移除sign 50 | unset($backData['sign']); 51 | 52 | // 公众号支付返回数组结构 53 | return $backData; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Common/Weixin/Data/Query/ChargeQueryData.php: -------------------------------------------------------------------------------- 1 | retData = [ 30 | 'appid' => $this->appId, 31 | 'mch_id' => $this->mchId, 32 | 'nonce_str' => $this->nonceStr, 33 | 'sign_type' => $this->signType, 34 | 35 | 'transaction_id' => $this->transaction_id, 36 | 'out_trade_no' => $this->out_trade_no, 37 | 38 | // 服务商 39 | 'sub_appid' => $this->sub_appid, 40 | 'sub_mch_id' => $this->sub_mch_id, 41 | ]; 42 | 43 | $this->retData = ArrayUtil::paraFilter($this->retData); 44 | } 45 | 46 | protected function checkDataParam() 47 | { 48 | $transaction_id = $this->transaction_id;// 微信交易号,查询效率高 49 | $order_no = $this->out_trade_no;// 商户订单号,查询效率低,不建议使用 50 | 51 | // 二者不能同时为空 52 | if (empty($transaction_id) && empty($order_no)) { 53 | throw new PayException('必须提供微信交易号或商户网站唯一订单号。建议使用微信交易号'); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/Common/Ali/Data/Charge/AppChargeData.php: -------------------------------------------------------------------------------- 1 | strval($this->body), 23 | 'subject' => strval($this->subject), 24 | 'out_trade_no' => strval($this->order_no), 25 | 'total_amount' => strval($this->amount), 26 | 27 | // 销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY 28 | 'product_code' => 'QUICK_MSECURITY_PAY', 29 | 'goods_type' => $this->goods_type, 30 | 'passback_params' => $this->return_param, 31 | // TODO 优惠信息待支持 业务扩展参数,待支持 32 | // 'promo_params' => '', 33 | // 'extend_params => '', 34 | 'disable_pay_channels' => $this->limitPay, 35 | 'store_id' => $this->store_id, 36 | ]; 37 | 38 | $timeExpire = $this->timeout_express; 39 | if (! empty($timeExpire)) { 40 | $express = floor(($timeExpire - strtotime($this->timestamp)) / 60); 41 | ($express > 0) && $content['timeout_express'] = $express . 'm';// 超时时间 统一使用分钟计算 42 | } 43 | 44 | return $content; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Common/Ali/Data/Charge/QrChargeData.php: -------------------------------------------------------------------------------- 1 | strval($this->order_no), 25 | // TODO 卖家支付宝id 26 | // 'seller_id' => '', 27 | 'total_amount' => strval($this->amount), 28 | // TODO 折扣金额 29 | // 'discountable_amount' => '', 30 | // TODO 业务扩展参数 订单商品列表信息,待支持 31 | // 'extend_params => '', 32 | // 'goods_detail' => '', 33 | 'subject' => strval($this->subject), 34 | 'body' => strval($this->body), 35 | 36 | 'operator_id' => $this->operator_id, 37 | 'store_id' => $this->store_id, 38 | 'terminal_id' => $this->terminal_id, 39 | ]; 40 | 41 | $timeExpire = $this->timeout_express; 42 | if (! empty($timeExpire)) { 43 | $express = floor(($timeExpire - strtotime($this->timestamp)) / 60); 44 | ($express > 0) && $content['timeout_express'] = $express . 'm';// 超时时间 统一使用分钟计算 45 | } 46 | 47 | return $content; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Common/Ali/Data/Charge/WapChargeData.php: -------------------------------------------------------------------------------- 1 | strval($this->body), 25 | 'subject' => strval($this->subject), 26 | 'out_trade_no' => strval($this->order_no), 27 | 'total_amount' => strval($this->amount), 28 | 29 | // 销售产品码,商家和支付宝签约的产品码,为固定值QUICK_WAP_PAY 30 | 'product_code' => 'QUICK_WAP_PAY', 31 | 'goods_type' => $this->goods_type, 32 | 'passback_params' => $this->return_param, 33 | // TODO 优惠信息待支持 业务扩展参数,待支持 34 | // 'promo_params' => '', 35 | // 'extend_params => '', 36 | 'disable_pay_channels' => $this->limitPay, 37 | 'store_id' => $this->store_id, 38 | // TODO 在收银台出现返回按钮 39 | 'quit_url' => $this->quit_url, 40 | ]; 41 | 42 | $timeExpire = $this->timeout_express; 43 | if (! empty($timeExpire)) { 44 | $express = floor(($timeExpire - strtotime($this->timestamp)) / 60); 45 | ($express > 0) && $content['timeout_express'] = $express . 'm';// 超时时间 统一使用分钟计算 46 | } 47 | 48 | return $content; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Utils/Rc4Encrypt.php: -------------------------------------------------------------------------------- 1 | key = $key; 18 | } 19 | 20 | /** 21 | * 设置key 22 | * @param $key 23 | * @author helei 24 | */ 25 | public function setKey($key) 26 | { 27 | $this->key = $key; 28 | } 29 | 30 | /** 31 | * rc4加密算法 32 | * @param $data 33 | * @return string 34 | * @throws \Exception 35 | */ 36 | public function encrypt($data) 37 | { 38 | $cipher = $box[] = $key[] = ''; 39 | 40 | $pwd_length = strlen($this->key); 41 | $data_length = strlen($data); 42 | 43 | for ($i = 0; $i < 256; $i++) { 44 | $key[$i] = ord($this->key[$i % $pwd_length]); 45 | $box[$i] = $i; 46 | } 47 | 48 | for ($j = $i = 0; $i < 256; $i++) { 49 | $j = ($j + $box[$i] + $key[$i]) % 256; 50 | $tmp = $box[$i]; 51 | $box[$i] = $box[$j]; 52 | $box[$j] = $tmp; 53 | } 54 | 55 | for ($a = $j = $i = 0; $i < $data_length; $i++) { 56 | $a = ($a + 1) % 256; 57 | $j = ($j + $box[$a]) % 256; 58 | 59 | $tmp = $box[$a]; 60 | $box[$a] = $box[$j]; 61 | $box[$j] = $tmp; 62 | 63 | $k = $box[(($box[$a] + $box[$j]) % 256)]; 64 | $cipher .= chr(ord($data[$i]) ^ $k); 65 | } 66 | 67 | return strtoupper(StrUtil::String2Hex($cipher)); 68 | } 69 | } -------------------------------------------------------------------------------- /src/Common/Cmb/Data/BindCardData.php: -------------------------------------------------------------------------------- 1 | agr_no; 30 | if (empty($agrNo) || mb_strlen($agrNo) > 30 || ! is_numeric($agrNo)) { 31 | throw new PayException('客户协议号。必须为纯数字串,不超过30位'); 32 | } 33 | } 34 | 35 | protected function getReqData() 36 | { 37 | $reqData = [ 38 | 'dateTime' => $this->dateTime, 39 | 'merchantSerialNo' => $this->serial_no ? $this->serial_no : '', 40 | 'agrNo' => $this->agr_no, 41 | 'branchNo' => $this->branchNo, 42 | 'merchantNo' => $this->merchantNo, 43 | 'userID' => $this->user_id ? $this->user_id : '', 44 | 'mobile' => $this->mobile ? $this->mobile : '', 45 | 'lon' => $this->lon ? $this->lon : '', 46 | 'lat' => $this->lat ? $this->lat : '', 47 | 'riskLevel' => $this->risk_level ? $this->risk_level : '', 48 | 'noticeUrl' => $this->signNoticeUrl ? $this->signNoticeUrl : '', 49 | 'noticePara' => $this->return_param ? $this->return_param : '', 50 | 'returnUrl' => $this->returnUrl ? $this->returnUrl : '', 51 | ]; 52 | 53 | // 这里不能进行过滤空值,招商的空值也要加入签名中 54 | return $reqData; 55 | } 56 | } -------------------------------------------------------------------------------- /src/HelperContext.php: -------------------------------------------------------------------------------- 1 | helper = new BindCardHelper($config); 37 | break; 38 | case Config::CMB_PUB_KEY: 39 | $this->helper = new PubKeyHelper($config); 40 | break; 41 | default: 42 | throw new PayException('当前仅支持:CMB_BIND CMB_PUB_KEY 操作'); 43 | } 44 | } catch (PayException $e) { 45 | throw $e; 46 | } 47 | } 48 | 49 | /** 50 | * 获取帮助操作 51 | * 52 | * @param array $data 53 | * 54 | * @return array 55 | * @throws PayException 56 | * @author helei 57 | */ 58 | public function helper(array $data) 59 | { 60 | if (! $this->helper instanceof BaseStrategy) { 61 | throw new PayException('请检查初始化是否正确'); 62 | } 63 | 64 | try { 65 | return $this->helper->handle($data); 66 | } catch (PayException $e) { 67 | throw $e; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/Client/Refund.php: -------------------------------------------------------------------------------- 1 | initRefund($channel, $config); 47 | } catch (PayException $e) { 48 | throw $e; 49 | } 50 | 51 | return static::$instance; 52 | } 53 | 54 | public static function run($channel, $config, $refundData) 55 | { 56 | if (! in_array($channel, self::$supportChannel)) { 57 | throw new PayException('sdk当前不支持该退款渠道,当前仅支持:' . implode(',', self::$supportChannel)); 58 | } 59 | 60 | try { 61 | $instance = self::getInstance($channel, $config); 62 | 63 | $ret = $instance->refund($refundData); 64 | } catch (PayException $e) { 65 | throw $e; 66 | } 67 | 68 | return $ret; 69 | } 70 | } -------------------------------------------------------------------------------- /src/Common/Weixin/Data/WxBaseData.php: -------------------------------------------------------------------------------- 1 | signType) { 45 | case 'MD5': 46 | $signStr .= '&key=' . $this->md5Key; 47 | $sign = md5($signStr); 48 | break; 49 | case 'HMAC-SHA256': 50 | $sign = base64_encode(hash_hmac('sha256', $signStr, $this->md5Key)); 51 | break; 52 | default: 53 | $sign = ''; 54 | } 55 | 56 | return strtoupper($sign); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Query/Cmb/CmbRefundQuery.php: -------------------------------------------------------------------------------- 1 | config->getewayUrl = 'https://payment.ebank.cmbchina.com/NetPayment/BaseHttp.dll?QuerySettledRefund'; 22 | if ($this->config->useSandbox) {// 测试 23 | $this->config->getewayUrl = 'http://121.15.180.66:801/netpayment_dl/BaseHttp.dll?QuerySettledRefund'; 24 | } 25 | 26 | return RefundQueryData::class; 27 | } 28 | 29 | protected function retData(array $ret) 30 | { 31 | $json = json_encode($ret, JSON_UNESCAPED_UNICODE); 32 | 33 | $postData = CmbConfig::REQ_FILED_NAME . '=' . $json; 34 | $retData = $this->sendReq($postData); 35 | 36 | if ($this->config->returnRaw) { 37 | $retData['channel'] = Config::CMB_REFUND; 38 | return $retData; 39 | } 40 | 41 | $list = $retData['dataList']; 42 | $list = str_replace('`', '', $list); 43 | $list = explode(PHP_EOL, $list); 44 | 45 | $header = array_shift($list); 46 | $header = explode(',', $header); 47 | 48 | foreach ($list as $key => $item) { 49 | $item = explode(',', $item); 50 | 51 | $list[$key] = array_combine($header, $item); 52 | } 53 | 54 | // 正确情况 55 | $retData = [ 56 | 'is_success' => 'T', 57 | 'response' => [ 58 | 'channel' => Config::CMB_REFUND, 59 | 'refund_data' => $list, 60 | ], 61 | ]; 62 | 63 | return $retData; 64 | } 65 | } -------------------------------------------------------------------------------- /src/TransferContext.php: -------------------------------------------------------------------------------- 1 | transfer = new AliTransfer($config); 39 | break; 40 | case Config::WX_TRANSFER: 41 | $this->transfer = new WxTransfer($config); 42 | break; 43 | default: 44 | throw new PayException('当前仅支持:ALI WEIXIN两个常量'); 45 | } 46 | } catch (PayException $e) { 47 | throw $e; 48 | } 49 | } 50 | 51 | /** 52 | * 通过环境类调用支付转款操作 53 | * 54 | * @param array $data 55 | * 56 | * @return array 57 | * @throws PayException 58 | * @author helei 59 | */ 60 | public function transfer(array $data) 61 | { 62 | if (! $this->transfer instanceof BaseStrategy) { 63 | throw new PayException('请检查初始化是否正确'); 64 | } 65 | 66 | try { 67 | return $this->transfer->handle($data); 68 | } catch (PayException $e) { 69 | throw $e; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Payment只有在大家的使用反馈中才能得到不断的完善。 2 | 3 | 我希望通过真实的项目来驱动它不断发展,在为工作带来方便的同时尽力保持它的简洁。 4 | 5 | # issue报Bug 6 | 由于Payment高度依赖第三方接口,因此第三方一个小的变动也会导致项目产生一个大版本号。当前主要有: 7 | - 2.x 应该没多少人使用了,已经放弃维护 8 | - 3.x 继续维护,只修bug,不做接口更新 9 | - 4.x 当前开发版本,均保持当前第三方的最新接口 10 | 11 | 由于版本比较多。因此报bug建议采用以下格式: 12 | 13 | [3/4.x]版本,在什么环境下(沙盒还是正式),调用了什么接口,出现了什么错误(最好有截图)。自己尝试过哪些办法去解决未达到预期效果 14 | 15 | 推荐所有的bug在提交时,先使用demo代码运行一下,看看能否通过。 16 | 17 | **只提供标题,或者没有重现步骤的,将不处理** 18 | 19 | # 贡献代码 20 | 请代码书写遵循以下规则: 21 | 22 | - [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) 23 | - 使用4个空格作为缩紧(不是tab) 24 | - 命名使用驼峰命名(不准使用拼音) 25 | - 请给类、方法、变量添加注释,注释需要包含:日期、修改人、含义 26 | 27 | ## 开发流程 28 | 29 | 1. Fork [helei112g/payment](https://github.com/helei112g/payment) 到本地 30 | 2. 创建新的分支: 31 | 32 | ```shell 33 | $ git checkout -b new_feature 34 | ``` 35 | 36 | 3. 编写代码 37 | 4. Push 到你的分支: 38 | 39 | ```shell 40 | $ git push origin new_feature 41 | ``` 42 | 43 | 5. 创建 Pull Request 并描述你完成的功能或者做出的修改 44 | 45 | 所提交的部分一定自己真实测试完毕,如果是新的支付功能,需要添加对应的demo以及 46 | 47 | 相关功能的文档(暂无开源文档地址,请根据功能名称,提供 `.md`的说明文档)。 48 | 49 | ## 代码说明 50 | 为了让大家快速理解代码结构,将项目相关结构图进行说明,这里从调用者的角度出发进行描述 51 | ![image](http://ol59nqr1i.bkt.clouddn.com/jiegou.jpeg) 52 | 53 | 这张图表现的是库的一个层次。用支付宝手机wap支付为例: 54 | 调用这只需要通过 55 | ```php 56 | Charge::run() 57 | ``` 58 | 这个方法即可完成所有调用。剩下的所有东西对调用者都应该是透明的 59 | 60 | 这个方法内部会首先通过 61 | ```php 62 | ChargeContext::initCharge() 63 | ``` 64 | 进行上下文的初始化,完成准备工作,并返回一个具体的对应支付实例,在这里是:`AliAppCharge` 65 | 66 | 接着内部会调用 67 | ```php 68 | AliAppCharge::charge() 69 | ``` 70 | 完成支付的请求,他会把结果返回给调用者 71 | 72 | 在 `AliAppCharge::charge()` 调用中完成了请求数据的组装,请求数据的签名,如果需要网络请求,会发送网络请求到支付宝网关。并把结果逐步返回。 73 | 74 | ## 核心类图 75 | 本库的所有功能,层次结构比较一致。这里以支付宝app支付为例,进行一下类图描述,方便大家以此进行类比 76 | 77 | ![uml](http://ol59nqr1i.bkt.clouddn.com/payment-uml.png) 78 | 79 | 如果看不清楚,可[点击下载](http://ol59nqr1i.bkt.clouddn.com/payment-uml.png) 80 | 81 | --------- 82 | **`遇到bug,90%都是秘钥相关导致的,微信可能与后台配置有关。请仔细检查。`** -------------------------------------------------------------------------------- /src/Client/Helper.php: -------------------------------------------------------------------------------- 1 | initHelper($channel, $config); 42 | } catch (PayException $e) { 43 | throw $e; 44 | } 45 | 46 | return static::$instance; 47 | } 48 | 49 | /** 50 | * @param string $channel 51 | * @param array $config 52 | * @param array $metadata 53 | * 54 | * @return mixed 55 | * @throws PayException 56 | */ 57 | public static function run($channel, $config, array $metadata = []) 58 | { 59 | if (! in_array($channel, self::$supportChannel)) { 60 | throw new PayException('sdk当前不支持该渠道,当前仅支持:' . implode(',', self::$supportChannel)); 61 | } 62 | 63 | try { 64 | $instance = self::getInstance($channel, $config); 65 | 66 | $ret = $instance->helper($metadata); 67 | } catch (PayException $e) { 68 | throw $e; 69 | } 70 | 71 | return $ret; 72 | } 73 | } -------------------------------------------------------------------------------- /src/Common/Ali/Data/Charge/WebChargeData.php: -------------------------------------------------------------------------------- 1 | strval($this->order_no), 30 | // 销售产品码,商家和支付宝签约的产品码,为固定值QUICK_WAP_PAY 31 | 'product_code' => 'FAST_INSTANT_TRADE_PAY', 32 | 'total_amount' => strval($this->amount), 33 | 'subject' => strval($this->subject), 34 | 'body' => strval($this->body), 35 | // TODO 订单包含的商品列表信息 待实现 36 | // 'goods_detail' => '', 37 | 'passback_params' => $this->return_param, 38 | // TODO 业务扩展参数,待支持 39 | // 'extend_params => '', 40 | 'goods_type' => $this->goods_type, 41 | 'disable_pay_channels' => $this->limitPay, 42 | 'store_id' => $this->store_id, 43 | 'qr_pay_mode' => $this->qr_mod, 44 | // TODO 设置二维码宽度 45 | // 'qrcode_width' => '',// qr_pay_mode = 4时有效。设置二维码宽度 46 | ]; 47 | 48 | $timeExpire = $this->timeout_express; 49 | if (! empty($timeExpire)) { 50 | $express = floor(($timeExpire - strtotime($this->timestamp)) / 60); 51 | ($express > 0) && $content['timeout_express'] = $express . 'm';// 超时时间 统一使用分钟计算 52 | } 53 | 54 | return $content; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Common/Cmb/Data/Query/ChargeQueryData.php: -------------------------------------------------------------------------------- 1 | transaction_id; 28 | $date = $this->date; 29 | $orderNo = $this->out_trade_no; 30 | 31 | if (empty($date) || mb_strlen($date) !== 8) { 32 | throw new PayException('商户订单日期必须提供,格式:yyyyMMdd'); 33 | } 34 | 35 | if ($bankSerialNo && mb_strlen($bankSerialNo) === 20) { 36 | $this->type = 'A'; 37 | } elseif ($orderNo && mb_strlen($bankSerialNo) <= 32) { 38 | $this->type = 'B'; 39 | } else { 40 | throw new PayException('必须设置商户订单信息或者招商流水号'); 41 | } 42 | } 43 | 44 | protected function getReqData() 45 | { 46 | $reqData = [ 47 | 'dateTime' => $this->dateTime, 48 | 'branchNo' => $this->branchNo, 49 | 'merchantNo' => $this->merchantNo, 50 | 'type' => $this->type, 51 | 'bankSerialNo' => $this->transaction_id ? $this->transaction_id : '', 52 | 53 | 'date' => $this->date ? $this->date : '', 54 | 'orderNo' => $this->out_trade_no ? $this->out_trade_no : '', 55 | 'operatorNo' => $this->operator_no ? $this->operator_no : '', 56 | ]; 57 | 58 | // 这里不能进行过滤空值,招商的空值也要加入签名中 59 | return $reqData; 60 | } 61 | } -------------------------------------------------------------------------------- /src/Refund/CmbRefund.php: -------------------------------------------------------------------------------- 1 | config->getewayUrl = 'https://payment.ebank.cmbchina.com/NetPayment/BaseHttp.dll?DoRefund'; 20 | if ($this->config->useSandbox) {// 测试 21 | $this->config->getewayUrl = 'http://121.15.180.66:801/NetPayment_dl/BaseHttp.dll?DoRefund'; 22 | } 23 | 24 | return RefundData::class; 25 | } 26 | 27 | protected function retData(array $ret) 28 | { 29 | $json = json_encode($ret, JSON_UNESCAPED_UNICODE); 30 | 31 | $postData = CmbConfig::REQ_FILED_NAME . '=' . $json; 32 | 33 | $rsqData = $this->sendReq($postData); 34 | 35 | if ($this->config->returnRaw) { 36 | $rsqData['channel'] = Config::CMB_REFUND; 37 | return $rsqData; 38 | } 39 | 40 | // 正确情况 41 | $retData = [ 42 | 'is_success' => 'T', 43 | 'response' => [ 44 | 'transaction_id' => $rsqData['bankSerialNo'],// 银行的退款流水号 45 | 'order_no' => $ret['reqData']['orderNo'], 46 | 'date' => $ret['reqData']['date'], 47 | 'refund_no' => trim($rsqData['refundSerialNo']),//退款流水号,商户生成 48 | 'refund_id' => $rsqData['refundRefNo'],// 银行的退款参考号 49 | 'currency' => $rsqData['currency'], 50 | 'refund_fee' => $rsqData['amount'], 51 | 'channel' => Config::CMB_REFUND, 52 | 'refund_time' => date('Y-m-d H:i:s', strtotime($rsqData['bankDate'] . $rsqData['bankTime'])), 53 | ], 54 | ]; 55 | 56 | return $retData; 57 | } 58 | } -------------------------------------------------------------------------------- /src/Trans/AliTransfer.php: -------------------------------------------------------------------------------- 1 | config->method = $this->method; 21 | return TransData::class; 22 | } 23 | 24 | protected function retData(array $data) 25 | { 26 | $reqData = parent::retData($data); 27 | 28 | try { 29 | $retData = $this->sendReq($reqData); 30 | } catch (PayException $e) { 31 | throw $e; 32 | } 33 | 34 | return $this->createBackData($retData); 35 | } 36 | 37 | /** 38 | * 处理返回的数据 39 | * @param array $data 40 | * @return array 41 | * @author helei 42 | */ 43 | protected function createBackData(array $data) 44 | { 45 | if ($this->config->returnRaw) { 46 | $retData['channel'] = Config::ALI_TRANSFER; 47 | return $retData; 48 | } 49 | 50 | if ($data['code'] !== '10000') { 51 | return $retData = [ 52 | 'is_success' => 'F', 53 | 'error' => $data['sub_msg'], 54 | 'channel' => Config::ALI_TRANSFER, 55 | ]; 56 | } 57 | 58 | $retData = [ 59 | 'is_success' => 'T', 60 | 'response' => [ 61 | 'trans_no' => $data['out_biz_no'],// 商户转账唯一订单号 62 | 'transaction_id' => $data['order_id'],// 支付宝转账单据号 63 | 'pay_date' => $data['pay_date'],// 支付时间 64 | 'channel' => Config::ALI_TRANSFER, 65 | ], 66 | ]; 67 | 68 | return $retData; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Charge/Ali/AliBarCharge.php: -------------------------------------------------------------------------------- 1 | config->method = $this->method; 25 | return BarChargeData::class; 26 | } 27 | 28 | /** 29 | * 处理扫码支付的返回值 30 | * @param array $ret 31 | * $data = [ 32 | 'code' => 10000, 33 | 'msg' => 'Success', 34 | 'buyer_logon_id' => 'day***@gmail.com', 35 | 'buyer_pay_amount' => '0.01', 36 | 'buyer_user_id' => '2088002162809334', 37 | 'fund_bill_list' => [ 38 | ['amount' => '0.01', 'fund_channel' => 'ALIPAYACCOUNT'], 39 | ], 40 | 'gmt_payment' => '2017-03-05 22:27:46', 41 | 'open_id' => '20880008025007264081318860117433', 42 | 'out_trade_no' => '14887240631516', 43 | 'point_amount' => '0.00', 44 | 'receipt_amount' => '0.01', 45 | 'total_amount' => '0.01', 46 | 'trade_no' => '2017030521001004330274482163', 47 | ]; 48 | * 49 | * @throws PayException 50 | * @return string 可生产二维码的uri 51 | * @author helei 52 | */ 53 | protected function retData(array $ret) 54 | { 55 | $reqData = parent::retData($ret); 56 | 57 | // 发起网络请求 58 | try { 59 | $data = $this->sendReq($reqData); 60 | } catch (PayException $e) { 61 | throw $e; 62 | } 63 | 64 | return $data; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Common/Weixin/Data/Query/RefundQueryData.php: -------------------------------------------------------------------------------- 1 | retData = [ 33 | 'appid' => $this->appId, 34 | 'mch_id' => $this->mchId, 35 | 'device_info' => $this->terminal_id, 36 | 'nonce_str' => $this->nonceStr, 37 | 'sign_type' => $this->signType, 38 | 39 | 'transaction_id' => $this->transaction_id, 40 | 'out_trade_no' => $this->out_trade_no, 41 | 'out_refund_no' => $this->refund_no, 42 | 'refund_id' => $this->refund_id, 43 | 44 | // 服务商 45 | 'sub_appid' => $this->sub_appid, 46 | 'sub_mch_id' => $this->sub_mch_id, 47 | ]; 48 | 49 | $this->retData = ArrayUtil::paraFilter($this->retData); 50 | } 51 | 52 | protected function checkDataParam() 53 | { 54 | $transactionId = $this->transaction_id;// 微信交易号,查询效率高 55 | $orderNo = $this->out_trade_no;// 商户订单号,查询效率低,不建议使用 56 | $refundNo = $this->refund_no;// 商户的退款单号 57 | $refundId = $this->refund_id;// 微信的退款交易号 58 | 59 | // 四者不能同时为空 60 | if (empty($transactionId) && empty($orderNo) && empty($refundNo) && empty($refundId)) { 61 | throw new PayException('查询退款 必须提供微信交易号、商户订单号、商户退款单号、微信退款交易号中的一种'); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/Client/Transfer.php: -------------------------------------------------------------------------------- 1 | initTransfer($channel, $config); 47 | } catch (PayException $e) { 48 | throw $e; 49 | } 50 | 51 | return static::$instance; 52 | } 53 | 54 | /** 55 | * @param $channel 56 | * @param $config 57 | * @param $metadata 58 | * 59 | * @return array 60 | * @throws PayException 61 | */ 62 | public static function run($channel, $config, $metadata) 63 | { 64 | if (! in_array($channel, self::$supportChannel)) { 65 | throw new PayException('sdk当前不支持该退款渠道,当前仅支持:' . implode(',', self::$supportChannel)); 66 | } 67 | 68 | try { 69 | $instance = self::getInstance($channel, $config); 70 | 71 | $ret = $instance->transfer($metadata); 72 | } catch (PayException $e) { 73 | throw $e; 74 | } 75 | 76 | return $ret; 77 | } 78 | } -------------------------------------------------------------------------------- /src/Common/Weixin/WechatHelper.php: -------------------------------------------------------------------------------- 1 | setSign(); 30 | 31 | $xml = DataParser::toXml($this->getData()); 32 | 33 | $url = self::SANDBOX_URL; 34 | 35 | $client = new Client([ 36 | 'timeout' => '10.0' 37 | ]); 38 | $options = [ 39 | 'body' => $xml, 40 | 'http_errors' => false 41 | ]; 42 | 43 | $response = $client->request('POST', self::SANDBOX_URL, $options); 44 | 45 | if ($response->getStatusCode() != '200') { 46 | throw new PayException('网络发生错误,请稍后再试curl返回码:' . $response->getReasonPhrase()); 47 | } 48 | // 格式化为数组 49 | $retData = DataParser::toArray($response->getBody()->getContents()); 50 | if (strtoupper($retData['return_code']) != 'SUCCESS') { 51 | throw new PayException('微信返回错误提示:' . $retData['return_msg']); 52 | } 53 | 54 | return $retData['sandbox_signkey']; 55 | } 56 | 57 | /** 58 | * 构建请求的数据 59 | */ 60 | protected function buildData() 61 | { 62 | $this->retData = [ 63 | 'mch_id' => $this->mchId, 64 | 'nonce_str' => StrUtil::getNonceStr(), 65 | ]; 66 | } 67 | 68 | protected function checkDataParam() 69 | { 70 | // TODO: Implement checkDataParam() method. 71 | } 72 | } -------------------------------------------------------------------------------- /src/Common/Ali/Data/RefundData.php: -------------------------------------------------------------------------------- 1 | trade_no;// 支付宝交易号,查询效率高 31 | $outTradeNo = $this->out_trade_no;// 商户订单号,查询效率低,不建议使用 32 | $refundAmount = $this->refund_fee; 33 | 34 | // 二者不能同时为空 35 | if (empty($outTradeNo) && empty($tradeNo)) { 36 | throw new PayException('必须提供支付宝交易号或者商户网站唯一订单号。建议使用支付宝交易号'); 37 | } 38 | 39 | if (empty($refundAmount) || ! is_numeric($refundAmount)) { 40 | throw new PayException('refund_fee 退款的金额,该金额不能大于订单金额,单位为元,支持两位小数'); 41 | } 42 | } 43 | 44 | /** 45 | * 业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递 46 | * 47 | * @return string 48 | */ 49 | protected function getBizContent() 50 | { 51 | $content = [ 52 | 'out_trade_no' => $this->out_trade_no, 53 | 'trade_no' => $this->trade_no, 54 | 'refund_amount' => $this->refund_fee, 55 | 'refund_reason' => $this->reason, 56 | 'out_request_no' => $this->refund_no, 57 | 'operator_id' => $this->operator_id, 58 | 'store_id' => $this->store_id, 59 | 'terminal_id' => $this->terminal_id, 60 | ]; 61 | 62 | return $content; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Common/Weixin/Data/Charge/AppChargeData.php: -------------------------------------------------------------------------------- 1 | scene_info; 18 | $sceneInfo = []; 19 | if ($info && is_array($info)) { 20 | $sceneInfo['store_info'] = $info; 21 | } 22 | 23 | $signData = [ 24 | 'appid' => trim($this->appId), 25 | 'mch_id' => trim($this->mchId), 26 | 'device_info' => $this->terminal_id, 27 | 'nonce_str' => $this->nonceStr, 28 | 'sign_type' => $this->signType, 29 | 'body' => trim($this->subject), 30 | //'detail' => json_encode($this->body, JSON_UNESCAPED_UNICODE), 31 | 'attach' => trim($this->return_param), 32 | 'out_trade_no' => trim($this->order_no), 33 | 'fee_type' => $this->feeType, 34 | 'total_fee' => $this->amount, 35 | 'spbill_create_ip' => trim($this->client_ip), 36 | 'time_start' => $this->timeStart, 37 | 'time_expire' => $this->timeout_express, 38 | //'goods_tag' => '订单优惠标记', 39 | 'notify_url' => $this->notifyUrl, 40 | 'trade_type' => $this->tradeType, //设置APP支付 41 | //'product_id' => '商品id', 42 | 'limit_pay' => $this->limitPay, // 指定不使用信用卡 43 | //'openid' => '用户标识', 44 | 'scene_info' => $sceneInfo ? json_encode($sceneInfo, JSON_UNESCAPED_UNICODE) : '', 45 | 46 | // 服务商 47 | 'sub_appid' => $this->sub_appid, 48 | 'sub_mch_id' => $this->sub_mch_id, 49 | ]; 50 | 51 | // 移除数组中的空值 52 | $this->retData = ArrayUtil::paraFilter($signData); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/RefundContext.php: -------------------------------------------------------------------------------- 1 | refund = new AliRefund($config); 42 | break; 43 | case Config::WX_REFUND: 44 | $this->refund = new WxRefund($config); 45 | break; 46 | case Config::CMB_REFUND: 47 | $this->refund = new CmbRefund($config); 48 | break; 49 | default: 50 | throw new PayException('当前仅支持:ALI WEIXIN CMB'); 51 | } 52 | } catch (PayException $e) { 53 | throw $e; 54 | } 55 | } 56 | 57 | /** 58 | * 通过环境类调用支付退款操作 59 | * 60 | * @param array $data 61 | * 62 | * @return array 63 | * @throws PayException 64 | * @author helei 65 | */ 66 | public function refund(array $data) 67 | { 68 | if (! $this->refund instanceof BaseStrategy) { 69 | throw new PayException('请检查初始化是否正确'); 70 | } 71 | 72 | try { 73 | return $this->refund->handle($data); 74 | } catch (PayException $e) { 75 | throw $e; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Client/Query.php: -------------------------------------------------------------------------------- 1 | initQuery($queryType, $config); 52 | } catch (PayException $e) { 53 | throw $e; 54 | } 55 | 56 | return static::$instance; 57 | } 58 | 59 | /** 60 | * @param string $queryType 61 | * @param array $config 62 | * @param array $metadata 63 | * @return array 64 | * @throws PayException 65 | */ 66 | public static function run($queryType, $config, $metadata) 67 | { 68 | if (! in_array($queryType, self::$supportType)) { 69 | throw new PayException('sdk当前不支持该类型查询,当前仅支持:' . implode(',', self::$supportType) . __LINE__); 70 | } 71 | 72 | try { 73 | $instance = self::getInstance($queryType, $config); 74 | 75 | $ret = $instance->query($metadata); 76 | } catch (PayException $e) { 77 | throw $e; 78 | } 79 | 80 | return $ret; 81 | } 82 | } -------------------------------------------------------------------------------- /src/Common/Weixin/Data/Charge/BarChargeData.php: -------------------------------------------------------------------------------- 1 | auth_code; 24 | if (empty($authCode)) { 25 | throw new PayException('扫码支付授权码,必须设置该参数.'); 26 | } 27 | } 28 | 29 | /** 30 | * 生成下单的数据 31 | */ 32 | protected function buildData() 33 | { 34 | $info = $this->scene_info; 35 | $sceneInfo = []; 36 | if ($info && is_array($info)) { 37 | $sceneInfo['store_info'] = $info; 38 | } 39 | 40 | $signData = [ 41 | 'appid' => trim($this->appId), 42 | 'mch_id' => trim($this->mchId), 43 | 'device_info' => $this->terminal_id, 44 | 'nonce_str' => $this->nonceStr, 45 | 'sign_type' => $this->signType, 46 | 'body' => trim($this->subject), 47 | //'detail' => json_encode($this->body, JSON_UNESCAPED_UNICODE), 48 | 'attach' => trim($this->return_param), 49 | 'out_trade_no' => trim($this->order_no), 50 | 'total_fee' => $this->amount, 51 | 'fee_type' => $this->feeType, 52 | 'spbill_create_ip' => trim($this->client_ip), 53 | //'goods_tag' => '订单优惠标记', 54 | 'limit_pay' => $this->limitPay, // 指定不使用信用卡 55 | 'auth_code' => $this->auth_code, 56 | 'scene_info' => $sceneInfo ? json_encode($sceneInfo, JSON_UNESCAPED_UNICODE) : '', 57 | 58 | // 服务商 59 | 'sub_appid' => $this->sub_appid, 60 | 'sub_mch_id' => $this->sub_mch_id, 61 | ]; 62 | 63 | // 移除数组中的空值 64 | $this->retData = ArrayUtil::paraFilter($signData); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Query/Cmb/CmbChargeQuery.php: -------------------------------------------------------------------------------- 1 | config->getewayUrl = 'https://payment.ebank.cmbchina.com/NetPayment/BaseHttp.dll?QuerySingleOrder'; 22 | if ($this->config->useSandbox) {// 测试 23 | $this->config->getewayUrl = 'http://121.15.180.66:801/NetPayment_dl/BaseHttp.dll?QuerySingleOrder'; 24 | } 25 | 26 | return ChargeQueryData::class; 27 | } 28 | 29 | protected function retData(array $ret) 30 | { 31 | $json = json_encode($ret, JSON_UNESCAPED_UNICODE); 32 | 33 | $postData = CmbConfig::REQ_FILED_NAME . '=' . $json; 34 | $ret = $this->sendReq($postData); 35 | 36 | if ($this->config->returnRaw) { 37 | $ret['channel'] = Config::CMB_CHARGE; 38 | return $ret; 39 | } 40 | 41 | // 正确情况 42 | $retData = [ 43 | 'is_success' => 'T', 44 | 'response' => [ 45 | 'amount' => $ret['orderAmount'], 46 | 'channel' => Config::CMB_CHARGE, 47 | 'order_no' => $ret['orderNo'], 48 | 'trade_state' => $this->getTradeStatus($ret['orderStatus']), 49 | 'transaction_id' => $ret['bankSerialNo'], 50 | 'time_end' => date('Y-m-d H:i:s', strtotime($ret['settleDate'] . $ret['settleTime'])),// Y-m-d H:i:s 51 | 'fee' => $ret['fee'],// 招商抽取的金额 52 | 'time_start' => date('Y-m-d H:i:s', strtotime($ret['bankDate'] . $ret['bankTime'])), 53 | 'discount_fee' => $ret['discountAmount'],// 优惠金额,格式:xxxx.xx 无优惠时返回0.00 54 | 'card_type' => $ret['cardType'],// 卡类型,02:一卡通;03:信用卡;07:他行卡 55 | 'return_param' => $ret['merchantPara'], 56 | ], 57 | ]; 58 | 59 | return $retData; 60 | } 61 | } -------------------------------------------------------------------------------- /src/NotifyContext.php: -------------------------------------------------------------------------------- 1 | notify = new AliNotify($config); 43 | break; 44 | case Config::WX_CHARGE: 45 | $this->notify = new WxNotify($config); 46 | break; 47 | case Config::CMB_CHARGE: 48 | $this->notify = new CmbNotify($config); 49 | break; 50 | default: 51 | throw new PayException('当前仅支持:ALI_CHARGE WX_CHARGE CMB_CHARGE 常量'); 52 | } 53 | } catch (PayException $e) { 54 | throw $e; 55 | } 56 | } 57 | 58 | /** 59 | * 返回异步通知的数据 60 | * @return array|false 61 | */ 62 | public function getNotifyData() 63 | { 64 | return $this->notify->getNotifyData(); 65 | } 66 | 67 | /** 68 | * 通过环境类调用支付异步通知 69 | * 70 | * @param PayNotifyInterface $notify 71 | * @return array 72 | * @throws PayException 73 | * @author helei 74 | */ 75 | public function notify(PayNotifyInterface $notify) 76 | { 77 | if (! $this->notify instanceof NotifyStrategy) { 78 | throw new PayException('请检查初始化是否正确'); 79 | } 80 | 81 | return $this->notify->handle($notify); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/notify.php: -------------------------------------------------------------------------------- 1 | errorMessage(); 44 | exit; 45 | } 46 | 47 | var_dump($ret); 48 | exit; 49 | -------------------------------------------------------------------------------- /src/Common/Cmb/Data/Query/RefundQueryData.php: -------------------------------------------------------------------------------- 1 | out_trade_no; 34 | $date = $this->date; 35 | $refundId = $this->refund_id;// 微信的退款交易号 36 | $refundNo = $this->refund_no;// 商户的退款单号 37 | 38 | if (empty($date)) { 39 | throw new PayException('商户退款日期,格式:yyyyMMdd'); 40 | } 41 | 42 | if (! empty($refundId)) {// 按银行退款流水号查单笔 43 | $this->out_trade_no = ''; 44 | $this->refund_no = ''; 45 | $this->type = 'A'; 46 | } elseif (! empty($orderNo) && ! empty($refundNo)) {// 按商户订单号+商户退款流水号查单笔 47 | $this->refund_id = ''; 48 | $this->type = 'B'; 49 | } elseif (! empty($orderNo)) {// 按商户订单号查退款 50 | $this->refund_id = ''; 51 | $this->refund_no = ''; 52 | $this->type = 'C'; 53 | } else { 54 | throw new PayException('请设置需要查询的商户订单号'); 55 | } 56 | } 57 | 58 | protected function getReqData() 59 | { 60 | $reqData = [ 61 | 'dateTime' => $this->dateTime, 62 | 'branchNo' => $this->branchNo, 63 | 'merchantNo' => $this->merchantNo, 64 | 'type' => $this->type, 65 | 'orderNo' => $this->out_trade_no ? $this->out_trade_no : '', 66 | 'date' => $this->date, 67 | 'merchantSerialNo' => $this->refund_no ? $this->refund_no : '', 68 | 'bankSerialNo' => $this->refund_id ? $this->refund_id : '', 69 | ]; 70 | 71 | // 这里不能进行过滤空值,招商的空值也要加入签名中 72 | return $reqData; 73 | } 74 | } -------------------------------------------------------------------------------- /src/Common/Ali/Data/Charge/ChargeBaseData.php: -------------------------------------------------------------------------------- 1 | subject; 45 | $orderNo = $this->order_no; 46 | $amount = $this->amount; 47 | $goodsType = $this->goods_type; 48 | $passBack = $this->return_param; 49 | 50 | // 检查 商品名称 与 商品描述 51 | if (empty($subject)) { 52 | throw new PayException('必须提供 商品的标题/交易标题/订单标题/订单关键字 等'); 53 | } 54 | 55 | // 检查订单号是否合法 56 | if (empty($orderNo) || mb_strlen($orderNo) > 64) { 57 | throw new PayException('订单号不能为空,并且长度不能超过64位'); 58 | } 59 | 60 | // 检查金额不能低于0.01 61 | if (bccomp($amount, Config::PAY_MIN_FEE, 2) === -1) { 62 | throw new PayException('支付金额不能低于 ' . Config::PAY_MIN_FEE . ' 元'); 63 | } 64 | 65 | // 检查商品类型 66 | if (empty($goodsType)) {// 默认为实物类商品 67 | $this->goods_type = 1; 68 | } elseif (! in_array($goodsType, [0 ,1])) { 69 | throw new PayException('商品类型可取值为:0-虚拟类商品 1-实物类商品'); 70 | } 71 | 72 | // 返回参数进行urlencode编码 73 | if (! empty($passBack) && ! is_string($passBack)) { 74 | throw new PayException('回传参数必须是字符串'); 75 | } 76 | if (!empty($passBack)) { 77 | $this->return_param = urlencode($passBack); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Trans/WxTransfer.php: -------------------------------------------------------------------------------- 1 | config->returnRaw) { 31 | $ret['channel'] = Config::WX_TRANSFER; 32 | return $ret; 33 | } 34 | 35 | // 请求失败,可能是网络 36 | if ($ret['return_code'] != 'SUCCESS') { 37 | return $retData = [ 38 | 'is_success' => 'F', 39 | 'error' => $ret['return_msg'], 40 | 'channel' => Config::WX_TRANSFER, 41 | ]; 42 | } 43 | 44 | // 业务失败 45 | if ($ret['result_code'] != 'SUCCESS') { 46 | return $retData = [ 47 | 'is_success' => 'F', 48 | 'error' => $ret['err_code_des'], 49 | 'channel' => Config::WX_TRANSFER, 50 | ]; 51 | } 52 | 53 | return $this->createBackData($ret); 54 | } 55 | 56 | /** 57 | * 返回数据 58 | * @param array $data 59 | * @return array 60 | */ 61 | protected function createBackData(array $data) 62 | { 63 | $retData = [ 64 | 'is_success' => 'T', 65 | 'response' => [ 66 | 'trans_no' => $data['partner_trade_no'], 67 | 'transaction_id' => $data['payment_no'], 68 | 'pay_date' => $data['payment_time'],// 企业付款成功时间 2015-05-19 15:26:59 69 | 'device_info' => $data['device_info'], 70 | 'channel' => Config::WX_TRANSFER, 71 | ], 72 | ]; 73 | 74 | return $retData; 75 | } 76 | 77 | /** 78 | * 企业转账,不需要签名,使用返回true 79 | * @param array $retData 80 | * @return bool 81 | */ 82 | protected function verifySign(array $retData) 83 | { 84 | return true; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Client/Charge.php: -------------------------------------------------------------------------------- 1 | initCharge($channel, $config); 55 | } catch (PayException $e) { 56 | throw $e; 57 | } 58 | 59 | return static::$instance; 60 | } 61 | 62 | /** 63 | * @param string $channel 64 | * @param array $config 65 | * @param array $metadata 66 | * 67 | * @return mixed 68 | * @throws PayException 69 | */ 70 | public static function run($channel, $config, $metadata) 71 | { 72 | if (! in_array($channel, self::$supportChannel)) { 73 | throw new PayException('sdk当前不支持该支付渠道,当前仅支持:' . implode(',', self::$supportChannel)); 74 | } 75 | 76 | try { 77 | $instance = self::getInstance($channel, $config); 78 | 79 | $ret = $instance->charge($metadata); 80 | } catch (PayException $e) { 81 | throw $e; 82 | } 83 | 84 | return $ret; 85 | } 86 | } -------------------------------------------------------------------------------- /src/Common/Weixin/Data/Charge/WapChargeData.php: -------------------------------------------------------------------------------- 1 | scene_info; 23 | if (! is_array($info) || empty($info)) { 24 | throw new PayException('微信 H5 支付,必须提供该参数'); 25 | } 26 | } 27 | 28 | protected function buildData() 29 | { 30 | $info = $this->scene_info; 31 | $sceneInfo = []; 32 | if ($info && is_array($info)) { 33 | $sceneInfo['h5_info'] = $info; 34 | } 35 | 36 | $signData = [ 37 | 'appid' => trim($this->appId), 38 | 'mch_id' => trim($this->mchId), 39 | 'device_info' => $this->terminal_id, 40 | 'nonce_str' => $this->nonceStr, 41 | 'sign_type' => $this->signType, 42 | 'body' => trim($this->subject), 43 | //'detail' => json_encode($this->body, JSON_UNESCAPED_UNICODE), 44 | 'attach' => trim($this->return_param), 45 | 'out_trade_no' => trim($this->order_no), 46 | 'fee_type' => $this->feeType, 47 | 'total_fee' => $this->amount, 48 | 'spbill_create_ip' => trim($this->client_ip), 49 | 'time_start' => $this->timeStart, 50 | 'time_expire' => $this->timeout_express, 51 | //'goods_tag' => '订单优惠标记', 52 | 'notify_url' => $this->notifyUrl, 53 | 'trade_type' => $this->tradeType, //设置APP支付 54 | //'product_id' => '商品id', 55 | 'limit_pay' => $this->limitPay, // 指定不使用信用卡 56 | 'openid' => $this->openid, 57 | 'scene_info' => $sceneInfo ? json_encode($sceneInfo, JSON_UNESCAPED_UNICODE) : '', 58 | 59 | // 服务商 60 | 'sub_appid' => $this->sub_appid, 61 | 'sub_mch_id' => $this->sub_mch_id, 62 | 'sub_openid' => $this->sub_openid, 63 | ]; 64 | 65 | // 移除数组中的空值 66 | $this->retData = ArrayUtil::paraFilter($signData); 67 | } 68 | } -------------------------------------------------------------------------------- /src/Client/Notify.php: -------------------------------------------------------------------------------- 1 | initNotify($type, $config); 47 | } catch (PayException $e) { 48 | throw $e; 49 | } 50 | 51 | return static::$instance; 52 | } 53 | 54 | /** 55 | * 执行异步工作 56 | * @param string $type 57 | * @param array $config 58 | * @param PayNotifyInterface $callback 59 | * @return array 60 | * @throws PayException 61 | */ 62 | public static function run($type, $config, $callback) 63 | { 64 | if (! in_array($type, self::$supportChannel)) { 65 | throw new PayException('sdk当前不支持该异步方式,当前仅支持:' . implode(',', self::$supportChannel)); 66 | } 67 | 68 | try { 69 | $instance = self::getInstance($type, $config); 70 | 71 | $ret = $instance->notify($callback); 72 | } catch (PayException $e) { 73 | throw $e; 74 | } 75 | 76 | return $ret; 77 | } 78 | 79 | /** 80 | * 返回异步通知的结果 81 | * @param $type 82 | * @param $config 83 | * @return array|false 84 | * @throws PayException 85 | */ 86 | public static function getNotifyData($type, $config) 87 | { 88 | try { 89 | $instance = self::getInstance($type, $config); 90 | 91 | return $instance->getNotifyData(); 92 | } catch (PayException $e) { 93 | throw $e; 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /src/Refund/AliRefund.php: -------------------------------------------------------------------------------- 1 | config->method = $this->method; 26 | return RefundData::class; 27 | } 28 | 29 | /** 30 | * 返回数据 31 | * @param array $data 32 | * @return array|string 33 | * @throws PayException 34 | */ 35 | protected function retData(array $data) 36 | { 37 | $reqData = parent::retData($data); 38 | 39 | try { 40 | $ret = $this->sendReq($reqData); 41 | } catch (PayException $e) { 42 | throw $e; 43 | } 44 | $content = \GuzzleHttp\json_decode($data['biz_content'], true); 45 | $refundNo = $content['out_request_no']; 46 | 47 | if ($this->config->returnRaw) { 48 | $ret['channel'] = Config::ALI_REFUND; 49 | $ret['refund_no'] = $refundNo; 50 | return $ret; 51 | } 52 | 53 | if ($ret['code'] !== '10000') { 54 | return [ 55 | 'is_success' => 'F', 56 | 'error' => $ret['sub_msg'], 57 | 'refund_no' => $refundNo 58 | ]; 59 | } 60 | 61 | $retData = [ 62 | 'is_success' => 'T', 63 | 'response' => [ 64 | 'transaction_id' => $ret['trade_no'], 65 | 'order_no' => $ret['out_trade_no'], 66 | 'logon_id' => $ret['buyer_logon_id'], 67 | 'fund_change' => $ret['fund_change'],// 本次退款是否发生了资金变化 68 | 'refund_fee' => $ret['refund_fee'],// 返回的总金额,这里支付宝会累计 69 | 'refund_time' => $ret['gmt_refund_pay'], 70 | 'refund_detail_item_list' => ArrayUtil::get($ret, 'refund_detail_item_list'),// 退款使用的资金渠道 71 | 'refund_no' => $refundNo, 72 | 'channel' => Config::ALI_REFUND, 73 | 'buyer_id' => $ret['buyer_user_id'], 74 | 'store_name' => ArrayUtil::get($ret, 'store_name'), 75 | ], 76 | ]; 77 | 78 | return $retData; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Common/Cmb/Data/RefundData.php: -------------------------------------------------------------------------------- 1 | refund_no;// 商户退款单号 35 | $date = $this->date;// 商户订单日期,支付时的订单日期 格式:yyyyMMdd 36 | $outTradeNo = $this->out_trade_no; 37 | $refundFee = $this->refund_fee; 38 | $operatorId = $this->operator_id; 39 | 40 | if (empty($date) || mb_strlen($date) !== 8) { 41 | throw new PayException('商户订单日期必须提供,格式:yyyyMMdd'); 42 | } 43 | 44 | if (empty($outTradeNo)) { 45 | throw new PayException('必须提供商户网站唯一订单号。'); 46 | } 47 | 48 | if (empty($refundNo) && mb_strlen($refundNo) < 21) { 49 | throw new PayException('退款流水号,商户生成,不能超过20位'); 50 | } 51 | 52 | if (empty($refundFee) || ! is_numeric($refundFee)) { 53 | throw new PayException('退款金额,格式xxxx.xx'); 54 | } 55 | 56 | if (empty($operatorId)) { 57 | throw new PayException('必须提供 商户结账系统的操作员号'); 58 | } 59 | } 60 | 61 | protected function getReqData() 62 | { 63 | $rc4 = new Rc4Encrypt($this->merKey); 64 | 65 | $reqData = [ 66 | 'dateTime' => $this->dateTime, 67 | 'branchNo' => $this->branchNo, 68 | 'merchantNo' => $this->merchantNo, 69 | 'date' => $this->date, 70 | 'orderNo' => $this->out_trade_no, 71 | 'refundSerialNo' => trim($this->refund_no), 72 | 'amount' => $this->refund_fee, 73 | 'desc' => $this->reason, 74 | 'operatorNo' => $this->operator_id, 75 | 'encrypType' => 'RC4',// 这里不让用户控制,直接采用 rc4加密 76 | 'pwd' => $rc4->encrypt($this->opPwd), 77 | ]; 78 | 79 | // 这里不能进行过滤空值,招商的空值也要加入签名中 80 | return $reqData; 81 | } 82 | } -------------------------------------------------------------------------------- /src/Common/Weixin/Data/Charge/ChargeBaseData.php: -------------------------------------------------------------------------------- 1 | order_no; 36 | $amount = $this->amount; 37 | $subject = $this->subject; 38 | $body = $this->body; 39 | $deviceInfo = $this->terminal_id; 40 | 41 | // 检查订单号是否合法 42 | if (empty($orderNo) || mb_strlen($orderNo) > 64) { 43 | throw new PayException('订单号不能为空,并且长度不能超过64位'); 44 | } 45 | 46 | // 检查金额不能低于0.01 47 | if (bccomp($amount, Config::PAY_MIN_FEE, 2) === -1) { 48 | throw new PayException('支付金额不能低于 ' . Config::PAY_MIN_FEE . ' 元'); 49 | } 50 | 51 | // 检查 商品名称 与 商品描述 52 | if (empty($subject) || empty($body)) { 53 | throw new PayException('必须提供商品名称与商品详情'); 54 | } 55 | 56 | // 初始 微信订单过期时间,最短失效时间间隔必须大于5分钟 57 | if ($this->timeout_express - strtotime($this->timeStart) < 5) { 58 | throw new PayException('必须设置订单过期时间,且需要大于5分钟.如果不正确请检查是否正确设置时区'); 59 | } else { 60 | $this->timeout_express = date('YmdHis', $this->timeout_express); 61 | } 62 | 63 | // 微信使用的单位位分.此处进行转化 64 | $this->amount = bcmul($amount, 100, 0); 65 | 66 | // 设置ip地址 67 | $clientIp = $this->client_ip; 68 | if (empty($clientIp)) { 69 | $this->client_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1'; 70 | } 71 | 72 | // 设置设备号 73 | if (empty($deviceInfo)) { 74 | $this->terminal_id = 'WEB'; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Common/Ali/Data/Charge/BarChargeData.php: -------------------------------------------------------------------------------- 1 | strval($this->order_no), 31 | 'scene' => $this->scene, 32 | 'auth_code' => $this->auth_code, 33 | 'product_code' => 'FACE_TO_FACE_PAYMENT', 34 | 35 | 'subject' => strval($this->subject), 36 | // TODO 支付宝用户ID 37 | // 'seller_id' => $this->partner, 38 | 39 | 'body' => strval($this->body), 40 | 'total_amount' => strval($this->amount), 41 | // TODO 折扣金额 42 | // 'discountable_amount' => '', 43 | // TODO 业务扩展参数 订单商品列表信息,待支持 44 | // 'extend_params => '', 45 | // 'goods_detail' => '', 46 | 47 | 'operator_id' => $this->operator_id, 48 | 'store_id' => $this->store_id, 49 | 'terminal_id' => $this->terminal_id, 50 | ]; 51 | 52 | $timeExpire = $this->timeout_express; 53 | if (! empty($timeExpire)) { 54 | $express = floor(($timeExpire - strtotime($this->timestamp)) / 60); 55 | ($express > 0) && $content['timeout_express'] = $express . 'm';// 超时时间 统一使用分钟计算 56 | } 57 | 58 | return $content; 59 | } 60 | 61 | protected function checkDataParam() 62 | { 63 | parent::checkDataParam(); // TODO: Change the autogenerated stub 64 | 65 | $scene = $this->scene; 66 | $authCode = $this->auth_code; 67 | 68 | if (empty($scene) || ! in_array($scene, ['bar_code', 'wave_code'])) { 69 | throw new PayException('支付场景 scene 必须设置 条码支付:bar_code 声波支付:wave_code'); 70 | } 71 | 72 | if (empty($authCode)) { 73 | throw new PayException('请提供支付授权码'); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Utils/DataParser.php: -------------------------------------------------------------------------------- 1 | "; 25 | foreach ($values as $key => $val) { 26 | if (is_numeric($val)) { 27 | $xml.="<".$key.">".$val.""; 28 | } else { 29 | $xml.="<".$key.">"; 30 | } 31 | } 32 | $xml.=""; 33 | return $xml; 34 | } 35 | 36 | /** 37 | * 将xml转为array 38 | * @param string $xml 39 | * @return array|false 40 | */ 41 | public static function toArray($xml) 42 | { 43 | if (!$xml) { 44 | return false; 45 | } 46 | 47 | // 检查xml是否合法 48 | $xml_parser = xml_parser_create(); 49 | if (!xml_parse($xml_parser, $xml, true)) { 50 | xml_parser_free($xml_parser); 51 | return false; 52 | } 53 | 54 | //将XML转为array 55 | //禁止引用外部xml实体 56 | libxml_disable_entity_loader(true); 57 | 58 | $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); 59 | 60 | return $data; 61 | } 62 | 63 | /** 64 | * google api 二维码生成【QRcode可以存储最多4296个字母数字类型的任意文本,具体可以查看二维码数据格式】 65 | * @param string $text 二维码包含的信息,可以是数字、字符、二进制信息、汉字。不能混合数据类型,数据必须经过UTF-8 URL-encoded 66 | * @param string $widthHeight 生成二维码的尺寸设置 67 | * @param string $ecLevel 可选纠错级别,QR码支持四个等级纠错,用来恢复丢失的、读错的、模糊的、数据。 68 | * L-默认:可以识别已损失的7%的数据 69 | * M-可以识别已损失15%的数据 70 | * Q-可以识别已损失25%的数据 71 | * H-可以识别已损失30%的数据 72 | * 73 | * @param string $margin 生成的二维码离图片边框的距离 74 | * 75 | * @return string 76 | */ 77 | public static function toQRimg($text, $widthHeight = '150', $ecLevel = 'L', $margin = '0') 78 | { 79 | $chl = urlencode($text); 80 | 81 | return "http://chart.apis.google.com/chart?chs={$widthHeight}x{$widthHeight}&cht=qr&chld={$ecLevel}|{$margin}&chl={$chl}"; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Query/Ali/AliRefundQuery.php: -------------------------------------------------------------------------------- 1 | config->method = $this->method; 24 | return RefundQueryData::class; 25 | } 26 | 27 | protected function retData(array $data) 28 | { 29 | $reqData = parent::retData($data); 30 | 31 | try { 32 | $ret = $this->sendReq($reqData); 33 | } catch (PayException $e) { 34 | throw $e; 35 | } 36 | 37 | if ($this->config->returnRaw) { 38 | $ret['channel'] = Config::ALI_REFUND; 39 | return $ret; 40 | } 41 | 42 | 43 | return $this->createBackData($ret); 44 | } 45 | 46 | /** 47 | * 返回数据给客户端 未完成,目前没有数据提供 48 | * @param array $data 49 | * @return array 50 | * @author helei 51 | */ 52 | protected function createBackData(array $data) 53 | { 54 | if ($data['code'] !== '10000') { 55 | return $retData = [ 56 | 'is_success' => 'F', 57 | 'error' => $data['sub_msg'], 58 | 'channel' => Config::ALI_REFUND, 59 | ]; 60 | } 61 | 62 | // 这里有个诡异情况。查询数据全部为空。仅返回一个成功的标记 63 | if (empty($data['out_trade_no'])) { 64 | return [ 65 | 'is_success' => 'T', 66 | 'msg' => strtolower($data['msg']), 67 | 'channel' => Config::ALI_REFUND, 68 | ]; 69 | } 70 | 71 | $retData = [ 72 | 'is_success' => 'T', 73 | 'response' => [ 74 | 'transaction_id' => ArrayUtil::get($data, 'trade_no'),// 支付宝订单号 75 | 'order_no' => ArrayUtil::get($data, 'out_trade_no'),// 商户订单号 76 | 'refund_no' => ArrayUtil::get($data, 'out_request_no'),// 本笔退款对应的退款请求号 77 | 'reason' => ArrayUtil::get($data, 'refund_reason'),// 退款理由 78 | 'amount' => ArrayUtil::get($data, 'total_amount'),// 订单总金额 79 | 'refund_amount' => ArrayUtil::get($data, 'refund_amount'),// 退款金额 80 | 'channel' => Config::ALI_REFUND, 81 | ] 82 | ]; 83 | 84 | return $retData; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | config->method = $this->method; 22 | return TransferQueryData::class; 23 | } 24 | 25 | protected function retData(array $data) 26 | { 27 | $reqData = parent::retData($data); // TODO: Change the autogenerated stub 28 | 29 | try { 30 | $ret = $this->sendReq($reqData); 31 | } catch (PayException $e) { 32 | throw $e; 33 | } 34 | 35 | if ($this->config->returnRaw) { 36 | $ret['channel'] = Config::ALI_TRANSFER; 37 | return $ret; 38 | } 39 | 40 | return $this->createBackData($ret); 41 | } 42 | 43 | /** 44 | * 返回数据给客户端 未完成,目前没有数据提供 45 | * @param array $data 46 | * @return array 47 | * @author helei 48 | */ 49 | protected function createBackData(array $data) 50 | { 51 | if ($data['code'] !== '10000') { 52 | return $retData = [ 53 | 'is_success' => 'F', 54 | 'error' => $data['sub_msg'], 55 | 'channel' => Config::ALI_TRANSFER, 56 | ]; 57 | } 58 | 59 | $retData = [ 60 | 'is_success' => 'T', 61 | 'response' => [ 62 | 'transaction_id' => ArrayUtil::get($data, 'order_id'),// 支付宝订单号 63 | 'status' => strtolower(ArrayUtil::get($data, 'status')), 64 | 'pay_date' => ArrayUtil::get($data, 'pay_date'),// 转账日期 65 | 'arrival_time_end' => ArrayUtil::get($data, 'arrival_time_end'),// 预计到账时间,转账到银行卡专用,格式为yyyy-MM-dd HH:mm:ss 66 | 'amount' => ArrayUtil::get($data, 'order_fee'),// 转账金额 67 | 'trans_no' => ArrayUtil::get($data, 'out_biz_no'),// 商户转账订单号 68 | 'channel' => Config::ALI_TRANSFER, 69 | ] 70 | ]; 71 | if (isset($data['error_code'])) { 72 | $retData['response']['error_code'] = ArrayUtil::get($data, 'error_code'); 73 | // 查询到的订单状态为FAIL失败或REFUND退票时,返回具体的原因。 74 | $retData['response']['fail_reason'] = ArrayUtil::get($data, 'fail_reason'); 75 | } 76 | 77 | return $retData; 78 | } 79 | } -------------------------------------------------------------------------------- /src/Common/Weixin/Data/Charge/QrChargeData.php: -------------------------------------------------------------------------------- 1 | scene_info; 26 | $sceneInfo = []; 27 | if ($info && is_array($info)) { 28 | $sceneInfo['store_info'] = $info; 29 | } 30 | 31 | $signData = [ 32 | 'appid' => trim($this->appId), 33 | 'mch_id' => trim($this->mchId), 34 | 'device_info' => $this->terminal_id, 35 | 'nonce_str' => $this->nonceStr, 36 | 'sign_type' => $this->signType, 37 | 'body' => trim($this->subject), 38 | //'detail' => json_encode($this->body, JSON_UNESCAPED_UNICODE), 39 | 'attach' => trim($this->return_param), 40 | 'out_trade_no' => trim($this->order_no), 41 | 'fee_type' => $this->feeType, 42 | 'total_fee' => $this->amount, 43 | 'spbill_create_ip' => trim($this->client_ip), 44 | 'time_start' => $this->timeStart, 45 | 'time_expire' => $this->timeout_express, 46 | //'goods_tag' => '订单优惠标记', 47 | 'notify_url' => $this->notifyUrl, 48 | 'trade_type' => $this->tradeType, //设置APP支付 49 | 'product_id' => $this->product_id, 50 | 'limit_pay' => $this->limitPay, // 指定不使用信用卡 51 | 'openid' => $this->openid, 52 | 'scene_info' => $sceneInfo ? json_encode($sceneInfo, JSON_UNESCAPED_UNICODE) : '', 53 | 54 | // 服务商 55 | 'sub_appid' => $this->sub_appid, 56 | 'sub_mch_id' => $this->sub_mch_id, 57 | 'sub_openid' => $this->sub_openid, 58 | ]; 59 | 60 | // 移除数组中的空值 61 | $this->retData = ArrayUtil::paraFilter($signData); 62 | } 63 | 64 | /** 65 | * 检查扫码支付必须的参数信息 66 | * @throws PayException 67 | */ 68 | protected function checkDataParam() 69 | { 70 | parent::checkDataParam(); // TODO: Change the autogenerated stub 71 | 72 | // 扫码支付,必须设置product_id 参数 73 | $productId = $this->product_id; 74 | if (empty($productId)) { 75 | throw new PayException('扫码支付,必须设置商品ID.'); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Refund/WxRefund.php: -------------------------------------------------------------------------------- 1 | config->returnRaw) { 32 | $ret['channel'] = Config::WX_REFUND; 33 | return $ret; 34 | } 35 | 36 | // 请求失败,可能是网络 37 | if ($ret['return_code'] != 'SUCCESS') { 38 | return $retData = [ 39 | 'is_success' => 'F', 40 | 'error' => $ret['return_msg'] 41 | ]; 42 | } 43 | 44 | // 业务失败 45 | if ($ret['result_code'] != 'SUCCESS') { 46 | return $retData = [ 47 | 'is_success' => 'F', 48 | 'error' => $ret['err_code_des'] 49 | ]; 50 | } 51 | 52 | return $this->createBackData($ret); 53 | } 54 | 55 | /** 56 | * 处理返回的数据 57 | * @param array $data 58 | * @return array 59 | * @author helei 60 | */ 61 | protected function createBackData(array $data) 62 | { 63 | // 将订单总金额金额处理为元 64 | $total_fee = bcdiv($data['total_fee'], 100, 2); 65 | // 将订单退款金额处理为元 66 | $refund_fee = bcdiv($data['refund_fee'], 100, 2); 67 | 68 | $retData = [ 69 | 'is_success' => 'T', 70 | 'response' => [ 71 | 'transaction_id' => $data['transaction_id'], 72 | 'order_no' => $data['out_trade_no'], 73 | 'refund_no' => $data['out_refund_no'], 74 | 'refund_id' => $data['refund_id'], 75 | 'refund_fee' => $refund_fee, 76 | 'refund_channel' => $data['refund_channel'], 77 | 'amount' => $total_fee, 78 | 'channel' => Config::WX_REFUND, 79 | 80 | 'coupon_refund_fee' => bcdiv($data['coupon_refund_fee'], 100, 2), 81 | 'coupon_refund_count' => $data['coupon_refund_count'], 82 | 'cash_fee' => bcdiv($data['cash_fee'], 100, 2), 83 | 'cash_refund_fee' => bcdiv($data['cash_refund_fee'], 100, 2), 84 | ], 85 | ]; 86 | 87 | return $retData; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Common/Ali/Data/TransData.php: -------------------------------------------------------------------------------- 1 | trans_no; 34 | $payeeType = $this->payee_type; 35 | $payeeAccount = $this->payee_account; 36 | $amount = $this->amount; 37 | $remark = $this->remark; 38 | 39 | if (empty($transNo)) { 40 | throw new PayException('请传入 商户转账唯一订单号'); 41 | } 42 | 43 | if (empty($payeeType) || ! in_array($payeeType, ['ALIPAY_USERID', 'ALIPAY_LOGONID'])) { 44 | throw new PayException('请传入收款账户类型'); 45 | } 46 | 47 | if (empty($payeeAccount)) { 48 | throw new PayException('请传入转账帐号'); 49 | } 50 | 51 | if (empty($amount) || bccomp($amount, 0, 2) !== 1) { 52 | throw new PayException('请输入转账金额,且大于0'); 53 | } 54 | 55 | if (bccomp($amount, Config::TRANS_FEE, 2) !== -1 && empty($remark)) { 56 | throw new PayException('转账金额大于等于' . Config::TRANS_FEE , '必须设置 remark'); 57 | } 58 | } 59 | 60 | /** 61 | * 业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递 62 | * 63 | * @return string 64 | */ 65 | protected function getBizContent() 66 | { 67 | $content = [ 68 | 'out_biz_no' => $this->trans_no,// 商户转账唯一订单号 69 | 'payee_type' => strtoupper($this->payee_type),// 收款方账户类型 70 | 'payee_account' => $this->payee_account,// 收款方账户 71 | 'amount' => $this->amount, 72 | 'payer_show_name' => $this->payer_show_name, 73 | 'payer_real_name' => $this->payer_real_name, 74 | 'payee_real_name' => $this->payee_real_name, 75 | 'remark' => $this->remark, 76 | ]; 77 | 78 | return $content; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Utils/StrUtil.php: -------------------------------------------------------------------------------- 1 | date('Ymd'), 20 | 'agr_no' => '430802198004014374', 21 | 'serial_no' => time() . rand(1000, 9999), 22 | 'mobile' => '13500007108', 23 | 'user_id' => '100', 24 | 'lon' => '', 25 | 'lat' => '', 26 | 'riskLevel' => '1', 27 | ]; 28 | 29 | try { 30 | $data = Helper::run(Config::CMB_BIND, $cmbConfig, $signData); 31 | } catch (PayException $e) { 32 | echo $e->errorMessage(); 33 | exit; 34 | } 35 | 36 | $btnText = '点我开始签约'; 37 | 38 | ?> 39 | 40 | 41 | 42 | 43 | 44 | 签约一网通支付 45 | 46 | 47 | 84 | 85 | 86 |
87 |
88 | '> 89 | 90 |
91 |
92 | 93 | -------------------------------------------------------------------------------- /src/Common/Weixin/Data/TransferData.php: -------------------------------------------------------------------------------- 1 | retData = [ 38 | 'mch_appid' => $this->appId, 39 | 'mchid' => $this->mchId, 40 | 'device_info' => $this->terminal_id, 41 | 'nonce_str' => $this->nonceStr, 42 | 'partner_trade_no' => $this->trans_no, 43 | 'openid' => $this->openid, 44 | 45 | 'check_name' => $this->check_name, 46 | 're_user_name' => $this->payer_real_name, 47 | 'amount' => $this->amount,// 此处需要处理单位为分 48 | 'desc' => $this->desc, 49 | 50 | // $_SERVER["REMOTE_ADDR"] 获取客户端接口。此处获取php所在机器的ip 如果无法获取,则使用该ip 51 | 'spbill_create_ip' => $this->client_ip, 52 | ]; 53 | 54 | $this->retData = ArrayUtil::paraFilter($this->retData); 55 | } 56 | 57 | /** 58 | * 检查相关参数是否设置 59 | * @author helei 60 | */ 61 | protected function checkDataParam() 62 | { 63 | $openId = $this->openid; 64 | $transNo = $this->trans_no; 65 | $checkName = $this->check_name; 66 | $realName = $this->payer_real_name; 67 | $amount = $this->amount; 68 | $clientIp = $this->client_ip; 69 | 70 | if (empty($openId)) { 71 | throw new PayException('商户appid下,某用户的openid 必须传入'); 72 | } 73 | 74 | if (empty($transNo)) { 75 | throw new PayException('商户订单号,需保持唯一性'); 76 | } 77 | 78 | if ($checkName !== 'NO_CHECK' && empty($realName)) { 79 | throw new PayException('请传入收款人真实姓名'); 80 | } 81 | 82 | // 微信使用的单位位分.此处进行转化 83 | $this->amount = bcmul($amount, 100, 0); 84 | if (empty($amount) || $amount < 0) { 85 | throw new PayException('转账金额错误'); 86 | } 87 | 88 | if (empty($clientIp)) { 89 | $this->client_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1'; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Common/Weixin/Data/Charge/PubChargeData.php: -------------------------------------------------------------------------------- 1 | openid; 28 | if (empty($openid)) { 29 | throw new PayException('用户在商户appid下的唯一标识,公众号支付,必须设置该参数.'); 30 | } 31 | 32 | $subMchId = $this->sub_mch_id;// 如果是服务商模式,则 sub_openid 必须提供 33 | $subOpenid = $this->sub_openid; 34 | if ($subMchId && empty($subOpenid)) { 35 | throw new PayException('公众号的服务商模式,必须提供 sub_openid 参数.'); 36 | } 37 | } 38 | 39 | protected function buildData() 40 | { 41 | $info = $this->scene_info; 42 | $sceneInfo = []; 43 | if ($info && is_array($info)) { 44 | $sceneInfo['store_info'] = $info; 45 | } 46 | 47 | $signData = [ 48 | 'appid' => trim($this->appId), 49 | 'mch_id' => trim($this->mchId), 50 | 'device_info' => $this->terminal_id, 51 | 'nonce_str' => $this->nonceStr, 52 | 'sign_type' => $this->signType, 53 | 'body' => trim($this->subject), 54 | //'detail' => json_encode($this->body, JSON_UNESCAPED_UNICODE), 55 | 'attach' => trim($this->return_param), 56 | 'out_trade_no' => trim($this->order_no), 57 | 'fee_type' => $this->feeType, 58 | 'total_fee' => $this->amount, 59 | 'spbill_create_ip' => trim($this->client_ip), 60 | 'time_start' => $this->timeStart, 61 | 'time_expire' => $this->timeout_express, 62 | //'goods_tag' => '订单优惠标记', 63 | 'notify_url' => $this->notifyUrl, 64 | 'trade_type' => $this->tradeType, //设置APP支付 65 | //'product_id' => '商品id', 66 | 'limit_pay' => $this->limitPay, // 指定不使用信用卡 67 | // 业务数据 68 | 'openid' => $this->openid, 69 | 'scene_info' => $sceneInfo ? json_encode($sceneInfo, JSON_UNESCAPED_UNICODE) : '', 70 | 71 | // 服务商 72 | 'sub_appid' => $this->sub_appid, 73 | 'sub_mch_id' => $this->sub_mch_id, 74 | 'sub_openid' => $this->sub_openid, 75 | ]; 76 | 77 | // 移除数组中的空值 78 | $this->retData = ArrayUtil::paraFilter($signData); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Utils/RsaEncrypt.php: -------------------------------------------------------------------------------- 1 | key = $key; 18 | } 19 | 20 | /** 21 | * 设置key 22 | * @param $key 23 | * @author helei 24 | */ 25 | public function setKey($key) 26 | { 27 | $this->key = $key; 28 | } 29 | 30 | /** 31 | * RSA签名, 此处秘钥是私有秘钥 32 | * @param string $data 签名的数组 33 | * @throws \Exception 34 | * @return string 35 | * @author helei 36 | */ 37 | public function encrypt($data) 38 | { 39 | if ($this->key === false) { 40 | return ''; 41 | } 42 | 43 | $res = openssl_get_privatekey($this->key); 44 | if (empty($res)) { 45 | throw new \Exception('您使用的私钥格式错误,请检查RSA私钥配置'); 46 | } 47 | 48 | openssl_sign($data, $sign, $res); 49 | openssl_free_key($res); 50 | 51 | //base64编码 52 | $sign = base64_encode($sign); 53 | return $sign; 54 | } 55 | 56 | /** 57 | * RSA解密 此处秘钥是用户私有秘钥 58 | * @param string $content 需要解密的内容,密文 59 | * @throws \Exception 60 | * @return string 61 | * @author helei 62 | */ 63 | public function decrypt($content) 64 | { 65 | if ($this->key === false) { 66 | return ''; 67 | } 68 | 69 | $res = openssl_get_privatekey($this->key); 70 | if (empty($res)) { 71 | throw new \Exception('您使用的私钥格式错误,请检查RSA私钥配置'); 72 | } 73 | 74 | //用base64将内容还原成二进制 75 | $content = base64_decode($content); 76 | //把需要解密的内容,按128位拆开解密 77 | $result = ''; 78 | for ($i = 0; $i < strlen($content)/128; $i++) { 79 | $data = substr($content, $i * 128, 128); 80 | openssl_private_decrypt($data, $decrypt, $res); 81 | $result .= $decrypt; 82 | } 83 | openssl_free_key($res); 84 | return $result; 85 | } 86 | 87 | /** 88 | * RSA验签 ,此处的秘钥,是第三方公钥 89 | * @param string $data 待签名数据 90 | * @param string $sign 要校对的的签名结果 91 | * @throws \Exception 92 | * @return bool 93 | * @author helei 94 | */ 95 | public function rsaVerify($data, $sign) 96 | { 97 | // 初始时,使用公钥key 98 | $res = openssl_get_publickey($this->key); 99 | if (empty($res)) { 100 | throw new \Exception('支付宝RSA公钥错误。请检查公钥文件格式是否正确'); 101 | } 102 | 103 | $result = (bool)openssl_verify($data, base64_decode($sign), $res); 104 | openssl_free_key($res); 105 | return $result; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Utils/Rsa2Encrypt.php: -------------------------------------------------------------------------------- 1 | key = $key; 18 | } 19 | 20 | /** 21 | * 设置key 22 | * @param $key 23 | * @author helei 24 | */ 25 | public function setKey($key) 26 | { 27 | $this->key = $key; 28 | } 29 | 30 | /** 31 | * RSA2签名, 此处秘钥是私有秘钥 32 | * @param string $data 签名的数组 33 | * @throws \Exception 34 | * @return string 35 | * @author helei 36 | */ 37 | public function encrypt($data) 38 | { 39 | if ($this->key === false) { 40 | return ''; 41 | } 42 | 43 | $res = openssl_get_privatekey($this->key); 44 | if (empty($res)) { 45 | throw new \Exception('您使用的私钥格式错误,请检查RSA私钥配置'); 46 | } 47 | 48 | openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256); 49 | openssl_free_key($res); 50 | 51 | //base64编码 52 | $sign = base64_encode($sign); 53 | return $sign; 54 | } 55 | 56 | /** 57 | * RSA2解密 此处秘钥是用户私有秘钥 58 | * @param string $content 需要解密的内容,密文 59 | * @throws \Exception 60 | * @return string 61 | * @author helei 62 | */ 63 | public function decrypt($content) 64 | { 65 | if ($this->key === false) { 66 | return ''; 67 | } 68 | 69 | $res = openssl_get_privatekey($this->key); 70 | if (empty($res)) { 71 | throw new \Exception('您使用的私钥格式错误,请检查RSA私钥配置'); 72 | } 73 | 74 | //用base64将内容还原成二进制 75 | $decodes = base64_decode($content); 76 | 77 | $str = ''; 78 | $dcyCont = ''; 79 | foreach ($decodes as $n => $decode) { 80 | if (!openssl_private_decrypt($decode, $dcyCont, $res)) { 81 | echo "
" . openssl_error_string() . "
"; 82 | } 83 | $str .= $dcyCont; 84 | } 85 | 86 | openssl_free_key($res); 87 | return $str; 88 | } 89 | 90 | /** 91 | * RSA2验签 ,此处的秘钥,是第三方公钥 92 | * @param string $data 待签名数据 93 | * @param string $sign 要校对的的签名结果 94 | * @throws \Exception 95 | * @return bool 96 | * @author helei 97 | */ 98 | public function rsaVerify($data, $sign) 99 | { 100 | // 初始时,使用公钥key 101 | $res = openssl_get_publickey($this->key); 102 | if (empty($res)) { 103 | throw new \Exception('支付宝RSA公钥错误。请检查公钥文件格式是否正确'); 104 | } 105 | 106 | $result = (bool)openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256); 107 | openssl_free_key($res); 108 | return $result; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Common/Cmb/Data/CmbBaseData.php: -------------------------------------------------------------------------------- 1 | signType) { 51 | case 'SHA-256': 52 | $sign = hash('sha256', "$signStr&{$this->merKey}"); 53 | break; 54 | default: 55 | $sign = ''; 56 | } 57 | 58 | return $sign; 59 | } 60 | 61 | /** 62 | * 构建数据 63 | */ 64 | protected function buildData() 65 | { 66 | $signData = [ 67 | // 公共参数 68 | 'version' => $this->version, 69 | 'charset' => $this->charset, 70 | 'signType' => $this->signType, 71 | 'reqData' => $this->getReqData(), 72 | ]; 73 | 74 | // 移除数组中的空值 75 | $this->retData = ArrayUtil::paraFilter($signData); 76 | } 77 | 78 | /** 79 | * 检查基本数据 80 | */ 81 | protected function checkDataParam() 82 | { 83 | $branchNo = $this->branchNo; 84 | $merchantNo = $this->merchantNo; 85 | 86 | if (empty($branchNo) || mb_strlen($branchNo) !== 4) { 87 | throw new PayException('商户分行号,4位数字'); 88 | } 89 | 90 | if (empty($merchantNo) || mb_strlen($merchantNo) !== 6) { 91 | throw new PayException('商户号,6位数字'); 92 | } 93 | } 94 | 95 | /** 96 | * 请求数据 97 | * 98 | * @return array 99 | */ 100 | abstract protected function getReqData(); 101 | } -------------------------------------------------------------------------------- /src/Notify/NotifyStrategy.php: -------------------------------------------------------------------------------- 1 | getNotifyData(); 32 | if ($notifyData === false) {// 失败,就返回错误 33 | return $this->replyNotify(false, '获取通知数据失败'); 34 | } 35 | 36 | // 检查异步通知返回的数据是否有误 37 | $checkRet = $this->checkNotifyData($notifyData); 38 | if ($checkRet === false) {// 失败,就返回错误 39 | return $this->replyNotify(false, '返回数据验签失败,可能数据被篡改'); 40 | } 41 | 42 | // 回调商户的业务逻辑 43 | $flag = $this->callback($notify, $notifyData); 44 | if ($flag) { 45 | $msg = 'OK'; 46 | } else { 47 | $msg = '商户逻辑调用出错'; 48 | } 49 | // 返回响应值 50 | return $this->replyNotify($flag, $msg); 51 | } 52 | 53 | /** 54 | * 回调商户的业务逻辑,根据返回的true 或者 false 向第三方返回数据 55 | * @param PayNotifyInterface $notify 56 | * @param array $notifyData 57 | * 58 | * @return boolean 59 | * @author helei 60 | */ 61 | protected function callback(PayNotifyInterface $notify, array $notifyData) 62 | { 63 | $data = $this->getRetData($notifyData); 64 | 65 | if ($data === false) { 66 | return false; 67 | } 68 | 69 | return $notify->notifyProcess($data); 70 | } 71 | 72 | /** 73 | * 获取移除通知的数据 并进行简单处理(如:格式化为数组) 74 | * 75 | * 如果获取数据失败,返回false 76 | * 77 | * @return array|false 78 | * @author helei 79 | */ 80 | abstract public function getNotifyData(); 81 | 82 | /** 83 | * 检查异步通知的数据是否合法 84 | * 85 | * 如果检查失败,返回false 86 | * 87 | * @param array $data 由 $this->getNotifyData() 返回的数据 88 | * @return boolean 89 | * @author helei 90 | */ 91 | abstract public function checkNotifyData(array $data); 92 | 93 | /** 94 | * 向客户端返回必要的数据 95 | * @param array $data 回调机构返回的回调通知数据 96 | * @return array|false 97 | * @author helei 98 | */ 99 | abstract protected function getRetData(array $data); 100 | 101 | /** 102 | * 根据返回结果,回答支付机构。是否回调通知成功 103 | * @param boolean $flag 每次返回的bool值 104 | * @param string $msg 通知信息,错误原因 105 | * @return mixed 106 | * @author helei 107 | */ 108 | abstract protected function replyNotify($flag, $msg = 'OK'); 109 | } 110 | -------------------------------------------------------------------------------- /src/Query/Wx/WxChargeQuery.php: -------------------------------------------------------------------------------- 1 | config->returnRaw) { 40 | $data['channel'] = Config::WX_CHARGE; 41 | return $data; 42 | } 43 | 44 | // 请求失败,可能是网络 45 | if ($data['return_code'] != 'SUCCESS') { 46 | return $retData = [ 47 | 'is_success' => 'F', 48 | 'error' => $data['return_msg'], 49 | 'channel' => Config::WX_CHARGE,// 支付查询 50 | ]; 51 | } 52 | 53 | // 业务失败 54 | if ($data['result_code'] != 'SUCCESS') { 55 | return $retData = [ 56 | 'is_success' => 'F', 57 | 'error' => $data['err_code_des'], 58 | 'channel' => Config::WX_CHARGE,// 支付查询 59 | ]; 60 | } 61 | 62 | // 正确 63 | return $this->createBackData($data); 64 | } 65 | 66 | /** 67 | * 返回数据给客户端 68 | * @param array $data 69 | * @return array 70 | * @author helei 71 | */ 72 | protected function createBackData(array $data) 73 | { 74 | // 将金额处理为元 75 | $totalFee = bcdiv($data['total_fee'], 100, 2); 76 | 77 | $retData = [ 78 | 'is_success' => 'T', 79 | 'response' => [ 80 | 'amount' => $totalFee, 81 | 'channel' => Config::WX_CHARGE,// 支付查询 82 | 'order_no' => $data['out_trade_no'], 83 | 'buyer_id' => $data['openid'], 84 | 'trade_state' => strtolower($data['trade_state']), 85 | 'transaction_id' => $data['transaction_id'], 86 | 'time_end' => date('Y-m-d H:i:s', strtotime($data['time_end'])), 87 | 'return_param' => $data['attach'], 88 | 'terminal_id' => $data['device_info'], 89 | 'trade_type' => $data['trade_type'], 90 | 'bank_type' => $data['bank_type'], 91 | 'trade_state_desc' => isset($data['trade_state_desc']) ? $data['trade_state_desc'] : '交易成功', 92 | ], 93 | ]; 94 | 95 | return ArrayUtil::paraFilter($retData); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /examples/cmb/charge.php: -------------------------------------------------------------------------------- 1 | $orderNo,// 招行订单位数变更为32位 23 | 'timeout_express' => time() + 600,// 表示必须 600s 内付款 24 | 'amount' => '0.01',// 单位为元 ,最小为0.01 25 | 'return_param' => 'tatata', 26 | 'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1',// 客户地址 27 | 'date' => date('Ymd'), 28 | 'agr_no' => '430802198004014358',// 建议用身份证 29 | 'serial_no' => time() . rand(1000, 9999),// 协议开通请求流水号,开通协议时必填 30 | 'user_id' => '888', 31 | 'mobile' => '13500007107', 32 | 'lon' => '', 33 | 'lat' => '', 34 | 'risk_level' => '3', 35 | ]; 36 | 37 | try { 38 | $data = Charge::run(Config::CMB_CHANNEL_APP, $cmbConfig, $payData); 39 | } catch (PayException $e) { 40 | echo $e->errorMessage(); 41 | exit; 42 | } 43 | 44 | $btnText = '点我开始支付'; 45 | 46 | ?> 47 | 48 | 49 | 50 | 51 | 52 | 一网通支付 53 | 54 | 55 | 92 | 93 | 94 |
95 |
96 | '> 97 | 98 |
99 |
100 | 101 | -------------------------------------------------------------------------------- /src/Common/Ali/Data/AliBaseData.php: -------------------------------------------------------------------------------- 1 | signType) { 51 | case 'RSA': 52 | $rsa = new RsaEncrypt($this->rsaPrivateKey); 53 | 54 | $sign = $rsa->encrypt($signStr); 55 | break; 56 | case 'RSA2': 57 | $rsa = new Rsa2Encrypt($this->rsaPrivateKey); 58 | 59 | $sign = $rsa->encrypt($signStr); 60 | break; 61 | default: 62 | $sign = ''; 63 | } 64 | 65 | return $sign; 66 | } 67 | 68 | /** 69 | * 构建 支付 加密数据 70 | * @author helei 71 | */ 72 | protected function buildData() 73 | { 74 | $bizContent = $this->getBizContent(); 75 | $bizContent = ArrayUtil::paraFilter($bizContent);// 过滤掉空值,下面不用在检查是否为空 76 | 77 | $signData = [ 78 | // 公共参数 79 | 'app_id' => $this->appId, 80 | 'method' => $this->method, 81 | 'format' => $this->format, 82 | 'charset' => $this->charset, 83 | 'sign_type' => $this->signType, 84 | 'timestamp' => $this->timestamp, 85 | 'version' => $this->version, 86 | 'notify_url' => $this->notifyUrl, 87 | 88 | // 业务参数 89 | 'biz_content' => json_encode($bizContent, JSON_UNESCAPED_UNICODE), 90 | ]; 91 | 92 | // 电脑支付 wap支付添加额外参数 93 | if (in_array($this->method, ['alipay.trade.page.pay', 'alipay.trade.wap.pay'])) { 94 | $signData['return_url'] = $this->returnUrl; 95 | } 96 | 97 | // 移除数组中的空值 98 | $this->retData = ArrayUtil::paraFilter($signData); 99 | } 100 | 101 | /** 102 | * 支付宝构建请求查询的数据 103 | * @return mixed 104 | */ 105 | abstract protected function getBizContent(); 106 | } 107 | -------------------------------------------------------------------------------- /src/QueryContext.php: -------------------------------------------------------------------------------- 1 | query = new AliChargeQuery($config); 46 | break; 47 | case Config::ALI_REFUND:// 支付宝退款订单查询 48 | $this->query = new AliRefundQuery($config); 49 | break; 50 | case Config::ALI_TRANSFER: 51 | $this->query = new AliTransferQuery($config); 52 | break; 53 | 54 | case Config::WX_CHARGE:// 微信支付订单查询 55 | $this->query = new WxChargeQuery($config); 56 | break; 57 | case Config::WX_REFUND:// 微信退款订单查询 58 | $this->query = new WxRefundQuery($config); 59 | break; 60 | case Config::WX_TRANSFER:// 微信转款订单查询 61 | $this->query = new WxTransferQuery($config); 62 | break; 63 | 64 | case Config::CMB_CHARGE:// 招商支付查询 65 | $this->query = new CmbChargeQuery($config); 66 | break; 67 | case Config::CMB_REFUND:// 招商退款查询 68 | $this->query = new CmbRefundQuery($config); 69 | break; 70 | default: 71 | throw new PayException('当前仅支持:ALI_CHARGE ALI_REFUND WX_CHARGE WX_REFUND WX_TRANSFER CMB_CHARGE CMB_REFUND'); 72 | } 73 | } catch (PayException $e) { 74 | throw $e; 75 | } 76 | } 77 | 78 | /** 79 | * 通过环境类调用支付异步通知 80 | * 81 | * @param array $data 82 | * // 二者设置一个即可 83 | * $data => [ 84 | * 'transaction_id' => '原付款支付宝交易号', 85 | * 'order_no' => '商户订单号', 86 | * ]; 87 | * 88 | * @return array 89 | * @throws PayException 90 | * @author helei 91 | */ 92 | public function query(array $data) 93 | { 94 | if (! $this->query instanceof BaseStrategy) { 95 | throw new PayException('请检查初始化是否正确'); 96 | } 97 | 98 | try { 99 | return $this->query->handle($data); 100 | } catch (PayException $e) { 101 | throw $e; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Query/Wx/WxTransferQuery.php: -------------------------------------------------------------------------------- 1 | config->returnRaw) { 33 | $data['channel'] = Config::WX_TRANSFER; 34 | return $data; 35 | } 36 | 37 | // 请求失败,可能是网络 38 | if ($data['return_code'] != 'SUCCESS') { 39 | return $retData = [ 40 | 'is_success' => 'F', 41 | 'error' => $data['return_msg'], 42 | 'channel' => Config::WX_TRANSFER, 43 | ]; 44 | } 45 | 46 | // 业务失败 47 | if ($data['result_code'] != 'SUCCESS') { 48 | return $retData = [ 49 | 'is_success' => 'F', 50 | 'error' => $data['err_code_des'], 51 | 'channel' => Config::WX_TRANSFER, 52 | ]; 53 | } 54 | 55 | // 正确 56 | return $this->createBackData($data); 57 | } 58 | 59 | /** 60 | * 返回数据给客户端 61 | * @param array $data 62 | * @return array 63 | * @author helei 64 | */ 65 | protected function createBackData(array $data) 66 | { 67 | // 将金额处理为元 68 | $amount = bcdiv($data['payment_amount'], 100, 2); 69 | 70 | $retData = [ 71 | 'is_success' => 'T', 72 | 'response' => [ 73 | 'trans_no' => $data['partner_trade_no'],// 商户单号 74 | 'transaction_id' => $data['detail_id'],// 付款单号 75 | 'status' => strtolower($data['status']),// 转账状态 76 | 'reason' => $data['reason'],// 失败原因 77 | 'openid' => $data['openid'], 78 | 'payee_name' => $data['transfer_name'],// 收款用户姓名 79 | 'amount' => $amount, 80 | 'pay_date' => $data['transfer_time'], 81 | 'desc' => $data['desc'],// 付款描述 82 | 'channel' => Config::WX_TRANSFER, 83 | ], 84 | ]; 85 | 86 | return $retData; 87 | } 88 | 89 | /** 90 | * @param array $data 91 | * @author helei 92 | * @throws PayException 93 | * @return array|string 94 | */ 95 | public function handle(array $data) 96 | { 97 | $buildClass = $this->getBuildDataClass(); 98 | 99 | try { 100 | $this->reqData = new $buildClass($this->config, $data); 101 | } catch (PayException $e) { 102 | throw $e; 103 | } 104 | 105 | $this->reqData->setSign(); 106 | 107 | $xml = DataParser::toXml($this->reqData->getData()); 108 | $ret = $this->sendReq($xml); 109 | 110 | return $this->retData($ret); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/Common/Cmb/Data/Charge/ChargeData.php: -------------------------------------------------------------------------------- 1 | amount; 33 | // 订单号交给支付系统自己检查 34 | 35 | // 检查金额不能低于0.01 36 | if (bccomp($amount, Config::PAY_MIN_FEE, 2) === -1) { 37 | throw new PayException('支付金额不能低于 ' . Config::PAY_MIN_FEE . ' 元'); 38 | } 39 | 40 | // 设置ip地址 41 | $clientIp = $this->client_ip; 42 | if (empty($clientIp)) { 43 | $this->client_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1'; 44 | } 45 | 46 | $timeExpire = $this->timeout_express; 47 | if (! empty($timeExpire)) { 48 | $express = floor(($timeExpire - strtotime($this->dateTime)) / 60); 49 | 50 | if ($express > CmbConfig::MAX_EXPIRE_TIME || $express < 0) {// 招商规定 51 | $this->timeout_express = CmbConfig::MAX_EXPIRE_TIME; 52 | } else { 53 | $this->timeout_express = $express; 54 | } 55 | } 56 | } 57 | 58 | /** 59 | * 请求数据 60 | */ 61 | protected function getReqData() 62 | { 63 | $reqData = [ 64 | 'dateTime' => $this->dateTime, 65 | 'branchNo' => $this->branchNo, 66 | 'merchantNo' => $this->merchantNo, 67 | 'date' => $this->date ? $this->date : date('Ymd', time()), 68 | 'orderNo' => $this->order_no, 69 | 'amount' => $this->amount, 70 | 'expireTimeSpan' => $this->timeout_express ? $this->timeout_express : '', 71 | 'payNoticeUrl' => $this->notifyUrl, 72 | 'payNoticePara' => $this->return_param ? $this->return_param : '', 73 | 'returnUrl' => $this->returnUrl ? $this->returnUrl : '', 74 | 'clientIP' => $this->client_ip, 75 | 'cardType' => $this->limitPay ? $this->limitPay : '', 76 | 'agrNo' => $this->agr_no, 77 | 'merchantSerialNo' => $this->serial_no ? $this->serial_no : '', 78 | 'userID' => $this->user_id ? $this->user_id : '', 79 | 'mobile' => $this->mobile ? $this->mobile : '', 80 | 'lon' => $this->lon ? $this->lon : '', 81 | 'lat' => $this->lat ? $this->lat : '', 82 | 'riskLevel' => $this->risk_level ? $this->risk_level : '', 83 | 'signNoticeUrl' => $this->signNoticeUrl ? $this->signNoticeUrl : '', 84 | 'signNoticePara' => $this->return_param ? $this->return_param : '', 85 | 86 | // 暂时先不支持下面方式 87 | 'extendInfo' => '', 88 | 'extendInfoEncrypType' => '', 89 | ]; 90 | 91 | // 这里不能进行过滤空值,招商的空值也要加入签名中 92 | return $reqData; 93 | } 94 | } -------------------------------------------------------------------------------- /src/Query/Ali/AliChargeQuery.php: -------------------------------------------------------------------------------- 1 | config->method = $this->method; 29 | return ChargeQueryData::class; 30 | } 31 | 32 | /** 33 | * 请求后得到的返回数据 34 | * @param array $data 35 | * @return array|mixed 36 | * @throws PayException 37 | */ 38 | protected function retData(array $data) 39 | { 40 | $data = parent::retData($data); // TODO: Change the autogenerated stub 41 | 42 | try { 43 | $ret = $this->sendReq($data); 44 | } catch (PayException $e) { 45 | throw $e; 46 | } 47 | 48 | if ($this->config->returnRaw) { 49 | $ret['channel'] = Config::ALI_CHARGE; 50 | return $ret; 51 | } 52 | 53 | return $this->createBackData($ret); 54 | } 55 | 56 | /** 57 | * 处理支付宝返回的数据,统一处理后返回 58 | * @param array $data 支付宝返回的数据 59 | * @return array 60 | * @author helei 61 | */ 62 | protected function createBackData(array $data) 63 | { 64 | // 新版本 65 | if ($data['code'] !== '10000') { 66 | return [ 67 | 'is_success' => 'F', 68 | 'error' => $data['sub_msg'], 69 | 'channel' => Config::ALI_CHARGE, 70 | ]; 71 | } 72 | 73 | // 正确情况 74 | $retData = [ 75 | 'is_success' => 'T', 76 | 'response' => [ 77 | 'channel' => Config::ALI_CHARGE, 78 | 'transaction_id' => $data['trade_no'],// 支付宝交易号 79 | 'order_no' => $data['out_trade_no'],// 商家订单号 80 | 'logon_id' => $data['buyer_logon_id'],// 买家支付宝账号 81 | 'trade_state' => $this->getTradeStatus($data['trade_status']), 82 | 'amount' => $data['total_amount'], 83 | 'receipt_amount' => $data['receipt_amount'],// 实收金额,单位为元,两位小数。 84 | 85 | 'pay_amount' => ArrayUtil::get($data, 'buyer_pay_amount'),// 买家实付金额,单位为元 86 | 'point_amount' => ArrayUtil::get($data, 'point_amount'),// 使用集分宝支付的金额 87 | 'invoice_amount' => ArrayUtil::get($data, 'invoice_amount'),// 交易中用户支付的可开具发票的金额,单位为元,两位小数 88 | 'time_end' => ArrayUtil::get($data, 'send_pay_date'),// 本次交易打款给卖家的时间 89 | 'store_id' => ArrayUtil::get($data, 'store_id'), 90 | 'terminal_id' => ArrayUtil::get($data, 'terminal_id'), 91 | 'store_name' => ArrayUtil::get($data, 'store_name'), 92 | 'buyer_id' => ArrayUtil::get($data, 'buyer_user_id'), 93 | 'fund_bill_list' => ArrayUtil::get($data, 'fund_bill_list', []),// 支付成功的各个渠道金额信息 94 | ], 95 | ]; 96 | 97 | return $retData; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /examples/aliconfig.php: -------------------------------------------------------------------------------- 1 | true,// 是否使用沙盒模式 25 | 26 | 'app_id' => '2016073100130857', 27 | 'sign_type' => 'RSA2',// RSA RSA2 28 | 29 | // 可以填写文件路径,或者密钥字符串 当前字符串是 rsa2 的支付宝公钥(开放平台获取) 30 | 'ali_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmBjJu2eA5HVSeHb7jZsuKKbPp3w0sKEsLTVvBKQOtyb7bjQRWMWBI7FrcwEekM1nIL+rDv71uFtgv7apMMJdQQyF7g6Lnn9niG8bT1ttB8Fp0eud5L97eRjFTOa9NhxUVFjGDqQ3b88o6u20HNJ3PRckZhNaFJJQzlahCpxaiIRX2umAWFkaeQu1fcjmoS3l3BLj8Ly2zRZAnczv8Jnkp7qsVYeYt01EPsAxd6dRZRw3uqsv9pxSvyEYA7GV7XL6da+JdvXECalQeyvUFzn9u1K5ivGID7LPUakdTBUDzlYIhbpU1VS8xO1BU3GYXkAaumdWQt7f+khoFoSw+x8yqQIDAQAB', 31 | 32 | // 可以填写文件路径,或者密钥字符串 我的沙箱模式,rsa与rsa2的私钥相同,为了方便测试 33 | 'rsa_private_key' => 'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/z+Ue/oS0GjO2 34 | myYrkdopw5qq6Ih/xlHBx0HBE0xA2dRinpMuZeI0LUUtN54UAUZbDz8rcaOCb0je 35 | loeYolw54tadcIw4Q2hbdeJPplldJZyi1BDYtBJZvAveeRSidHdmBSUtOtCBXUBl 36 | JUP3I8/R4c34Ii4Pm/K4vmhwLf/zqZAedKGhYP6m5q+p8sfBHRPy97/KluLPiSTR 37 | FqGSRmd0IitUGK+KQ5qsAfJXyN1oVR4jBYaxfx7dWkTWmxAfNqtKfMvu2a5lH6hv 38 | ClN+w4RUDBu3939bLjCYKcAomkv3QMquMP46m+D8Ny+3mGk5L9Ul4jyxlFTlV4L4 39 | JM3g/02xAgMBAAECggEBALZliwseHDLnd6V9g56K41ozlzBOTv6yJ6yNPgnLwAcr 40 | HLtq76p/V8smAVIuQTPkwnJ03S0CsumlyTVhDzAltG2XN14fWDdoYiQWxU3YccIR 41 | shFkd2CaW5jZKLA1k1moRqHM4r1P4FYjxshn12l7tHNwtdvvJL3THcxvxABovauF 42 | OVtznpRlnfJLjn2Lg+xNsxaYy3zL8L6nL7MXUWLKvmLiZn64PFcw7cf+9n2exRDs 43 | wn0wDCpypGqOVVXVFeZaXTwmOoxgIUAZfAExdLtabGGCAz1lTsA0+r4DW2nSTe8C 44 | Fy1Db+fcCTm+uQ3y6jDwuS3tB8V+PQKog3+ReZp/9sECgYEA/NEr+ln6DTy7u4rC 45 | Wq7mixRJ1kaiAUph/hADrUwhkMiUapSMNAIXblFB+BQUjFZQmXEbcvz0Y70g9Zi9 46 | JCXVTiDTBe7jj/FK63MU0F9KY5OducpVV+RhSpNy/i1M2qeW4gO351PpPHUpRUYr 47 | GkYvAKktqrSOdBEWD3IeKLYDXxMCgYEAwjoavGjWzD9Xckbpb8yrQ+gHfLeWDKh7 48 | BgvoBGagyqbzIOZU9wg3dSQ2F5eMWDxWVRGqap3fIHxcA0/VMqXG1DrvSIUC4SE8 49 | Zys515fR00c9h3W3IugHnKgdYcV7nZrJoPZXlMjPOo39FCBnfbrUOgnKwxMlz3lV 50 | vC6465ODhKsCgYEAmUtTuTd5kTE0O+FFO6s1iztAEjc94D5z8JNRR3EUITAeHgn4 51 | gUiLYI7Qy1WRqA5mTMPyeuS6Ywe4xnJYrWRrVDY+/if9v7f1T5K2GirNdld5mb// 52 | w41tGMUTQt/A7AwWRvEuP4v3rnr0DVcgp4vK0EHEuO9GOUZq8+6kLtc+cBUCgYBF 53 | J/kzEsVAjmEtkHA33ZExqaFY1+l2clrziTPAtWYVIiK5mSmxl9xfOliER/KxzDIV 54 | MigStEmpQH5ms3s/AGXuVVmz4aBn1rSyK2L6D9WnO9t9qv1dUW68aeOkV3OvZ1jZ 55 | lj0S/flDaSEulGclDmvYinoGwX+aAyLy0VQIlUqj5wKBgHEUEf7YDnvw/IBnF1E4 56 | 983/7zBx9skoHhpEZsh2+1or7LIw6z0m3lsNBnK0MZZBmW/7HwOtVfhXUUPbVrOJ 57 | di70YoMynX3gjK3LTXhzISheZgcNRKTqiJgVunPokJxQRyYcAfaQeuIm9O8cCPE1 58 | rZpNAzCdd4NSj83UZRm3YOmC', 59 | 60 | 'limit_pay' => [ 61 | //'balance',// 余额 62 | //'moneyFund',// 余额宝 63 | //'debitCardExpress',// 借记卡快捷 64 | //'creditCard',//信用卡 65 | //'creditCardExpress',// 信用卡快捷 66 | //'creditCardCartoon',//信用卡卡通 67 | //'credit_group',// 信用支付类型(包含信用卡卡通、信用卡快捷、花呗、花呗分期) 68 | ],// 用户不可用指定渠道支付当有多个渠道时用“,”分隔 69 | 70 | // 与业务相关参数 71 | 'notify_url' => 'https://helei112g.github.io/v1/notify/ali', 72 | 'return_url' => 'https://helei112g.github.io/', 73 | 74 | 'return_raw' => false,// 在处理回调时,是否直接返回原始数据,默认为 true 75 | ]; 76 | -------------------------------------------------------------------------------- /src/Utils/ArrayUtil.php: -------------------------------------------------------------------------------- 1 |