Commit 24a88c67 by wangyu

feat: 新增微信企业付款到银行卡及付款订单查询相关内容

1 parent 8a2b761c
...@@ -39,4 +39,4 @@ try { ...@@ -39,4 +39,4 @@ try {
exit; exit;
} }
echo json_encode($ret, JSON_UNESCAPED_UNICODE); echo json_encode($ret, JSON_UNESCAPED_UNICODE);
\ No newline at end of file
...@@ -24,4 +24,4 @@ return [ ...@@ -24,4 +24,4 @@ return [
'redirect_url' => 'https://helei112g.github.io/',// 如果是h5支付,可以设置该值,返回到指定页面 'redirect_url' => 'https://helei112g.github.io/',// 如果是h5支付,可以设置该值,返回到指定页面
'return_raw' => false,// 在处理回调时,是否直接返回原始数据,默认为true 'return_raw' => false,// 在处理回调时,是否直接返回原始数据,默认为true
]; ];
\ No newline at end of file
-----BEGIN CERTIFICATE-----
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
-----END CERTIFICATE-----
\ No newline at end of file
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm 43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
-----END CERTIFICATE----- PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
\ No newline at end of file YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
...@@ -39,6 +39,7 @@ class SwPubCharge extends SwBaseStrategy ...@@ -39,6 +39,7 @@ class SwPubCharge extends SwBaseStrategy
* ]; * ];
* ``` * ```
* *
* @throws \Payment\Common\PayException
*/ */
protected function retData(array $ret) protected function retData(array $ret)
{ {
......
...@@ -28,7 +28,8 @@ class Query ...@@ -28,7 +28,8 @@ class Query
Config::WX_CHARGE, Config::WX_CHARGE,
Config::WX_REFUND, Config::WX_REFUND,
Config::WX_RED, Config::WX_RED,
Config::WX_TRANSFER, Config::WX_TRANSFER, //微信企业付款到零钱
Config::WX_PAY_BANK, //微信企业付款到银行卡
Config::CMB_CHARGE, Config::CMB_CHARGE,
Config::CMB_REFUND, Config::CMB_REFUND,
...@@ -87,4 +88,5 @@ class Query ...@@ -87,4 +88,5 @@ class Query
return $ret; return $ret;
} }
}
\ No newline at end of file }
<?php <?php
/** /**
* Created by PhpStorm. * Created by PhpStorm.
* *
* Date: 2017/3/7 * Date: 2017/3/7
* Time: 下午3:16 * Time: 下午3:16
*/ */
...@@ -19,7 +19,8 @@ class Transfer ...@@ -19,7 +19,8 @@ class Transfer
private static $supportChannel = [ private static $supportChannel = [
Config::ALI_TRANSFER,// 支付宝 Config::ALI_TRANSFER,// 支付宝
Config::WX_TRANSFER,// 微信 Config::WX_TRANSFER, //微信企业付款到零钱
Config::WX_PAY_BANK, //微信企业付款到银行卡
'cmb_wallet',// 招行一网通 'cmb_wallet',// 招行一网通
...@@ -36,7 +37,6 @@ class Transfer ...@@ -36,7 +37,6 @@ class Transfer
{ {
if (is_null(self::$instance)) { if (is_null(self::$instance)) {
static::$instance = new TransferContext(); static::$instance = new TransferContext();
try { try {
static::$instance->initTransfer($channel, $config); static::$instance->initTransfer($channel, $config);
} catch (PayException $e) { } catch (PayException $e) {
...@@ -57,13 +57,12 @@ class Transfer ...@@ -57,13 +57,12 @@ class Transfer
*/ */
public static function run($channel, $config, $metadata) public static function run($channel, $config, $metadata)
{ {
if (! in_array($channel, self::$supportChannel)) { if (!in_array($channel, self::$supportChannel)) {
throw new PayException('sdk当前不支持该退款渠道,当前仅支持:' . implode(',', self::$supportChannel)); throw new PayException('sdk当前不支持该退款渠道,当前仅支持:' . implode(',', self::$supportChannel));
} }
try { try {
$instance = self::getInstance($channel, $config); $instance = self::getInstance($channel, $config);
$ret = $instance->transfer($metadata); $ret = $instance->transfer($metadata);
} catch (PayException $e) { } catch (PayException $e) {
throw $e; throw $e;
...@@ -71,4 +70,5 @@ class Transfer ...@@ -71,4 +70,5 @@ class Transfer
return $ret; return $ret;
} }
}
\ No newline at end of file }
...@@ -28,8 +28,7 @@ class PubChargeData extends ChargeBaseData ...@@ -28,8 +28,7 @@ class PubChargeData extends ChargeBaseData
// 公众号支付,必须设置openid // 公众号支付,必须设置openid
$payType = $this->pay_type;//010微信,020支付宝,060qq钱包,090口碑,100翼支付 $payType = $this->pay_type;//010微信,020支付宝,060qq钱包,090口碑,100翼支付
if (($payType == '010' || $payType == '020')) { if (($payType == '010' || $payType == '020')) {
if (empty($this->open_id)) if (empty($this->open_id)) throw new PayException('用户在商户appid下的唯一标识,用户标识(微信openid,支付宝userid),pay_type为010及020时需要传入.');
throw new PayException('用户在商户appid下的唯一标识,用户标识(微信openid,支付宝userid),pay_type为010及020时需要传入.');
} }
$this->service_id = '012'; $this->service_id = '012';
......
<?php
/**
*
* @createTime: 2016-08-04 09:42
* @description:
*/
namespace Payment\Common\Weixin\Data;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* Class TransferData
*
* 微信当前也仅支持单笔付款,不支持批量
*
* @property string $trans_no 商户转账唯一订单号
* @property string $openid 商户appid下,某用户的openid
* @property string $check_name
* - NO_CHECK:不校验真实姓名
* - FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账)
* - OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
*
* @property string $payer_real_name 收款用户真实姓名。
* @property string $amount 转账金额,单位:元。 只支持2位小数,小数点前最大支持13位,金额必须大于0。
* @property string $desc 企业付款操作说明信息。必填。
* @property string $client_ip 调用接口的机器Ip地址
*
* @package Payment\Common\Weixin\Data
*
*/
class TransferData extends WxBaseData
{
protected function buildData()
{
$this->retData = [
'mch_appid' => $this->appId,
'mchid' => $this->mchId,
'device_info' => $this->terminal_id,
'nonce_str' => $this->nonceStr,
'partner_trade_no' => $this->trans_no,
'openid' => $this->openid,
'check_name' => $this->check_name,
're_user_name' => $this->payer_real_name,
'amount' => $this->amount,// 此处需要处理单位为分
'desc' => $this->desc,
// $_SERVER["REMOTE_ADDR"] 获取客户端接口。此处获取php所在机器的ip 如果无法获取,则使用该ip
'spbill_create_ip' => $this->client_ip,
];
$this->retData = ArrayUtil::paraFilter($this->retData);
}
/**
* 检查相关参数是否设置
*
*/
protected function checkDataParam()
{
$openId = $this->openid;
$transNo = $this->trans_no;
$checkName = $this->check_name;
$realName = $this->payer_real_name;
$amount = $this->amount;
$clientIp = $this->client_ip;
if (empty($openId)) {
throw new PayException('商户appid下,某用户的openid 必须传入');
}
if (empty($transNo)) {
throw new PayException('商户订单号,需保持唯一性');
}
if ($checkName !== 'NO_CHECK' && empty($realName)) {
throw new PayException('请传入收款人真实姓名');
}
// 微信使用的单位位分.此处进行转化
$this->amount = bcmul($amount, 100, 0);
if (empty($amount) || $amount < 0) {
throw new PayException('转账金额错误');
}
if (empty($clientIp)) {
$this->client_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
}
}
}
<?php
namespace Payment\Common\Weixin\Data;
use Payment\Common\PayException;
use Payment\Utils\ArrayUtil;
/**
* 企业付款到银行卡
* Class PayBankData
*
* @package Payment\Common\Weixin\Data
*
* @property string $trans_no 商户唯一订单号
* @property string $payer_bank_no 收款方银行卡号(采用标准RSA算法,公钥由微信侧提供)
* @property string $payer_real_name 收款方用户名(采用标准RSA算法,公钥由微信侧提供)
* @property string $payer_bank_code 银行卡所在开户行编号
* - 银行编号列表:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_4
*
* @property string $amount 付款金额,单位:元。只支持2位小数,小数点前最大支持13位,金额必须大于0
* @property string $desc 企业付款到银行卡付款说明,即订单备注
*/
class PayBankData extends WxBaseData
{
/**
* 构建用于支付的签名相关数据
* @return array|void
* @createTime 2019/10/10 12:37
*/
protected function buildData()
{
$this->retData = [
// 'mch_appid' => $this->appId,
'mchid' => $this->mchId,
'partner_trade_no' => $this->trans_no,
'nonce_str' => $this->nonceStr,
'enc_bank_no' => $this->payer_bank_no,
'enc_true_name' => $this->payer_real_name,
'bank_code' => $this->payer_bank_code,
'amount' => $this->amount, //此处需要处理单位为分
'desc' => $this->desc,
];
$this->retData = ArrayUtil::paraFilter($this->retData);
}
/**
* 检查相关参数是否设置
* @return mixed|void
* @throws PayException
* @createTime 2019/10/10 14:03
*/
protected function checkDataParam()
{
$transNo = $this->trans_no;
$bankNo = $this->payer_bank_no;
$bankCode = $this->payer_bank_code;
$realName = $this->payer_real_name;
$amount = $this->amount;
if (empty($transNo)) throw new PayException('企业付款单号不能为空,且需保持唯一性');
if (strlen($transNo) < 8 || strlen($transNo) > 32) throw new PayException('企业付款单号最短8位最长32位');
if (empty($bankNo)) throw new PayException('请传入收款方银行卡号');
$this->payer_bank_no = $this->encryptByRSA($bankNo);
if (empty($bankCode)) throw new PayException('请传入收款方银行卡所在开户行编号');
if (empty($realName)) throw new PayException('请传入收款方用户名');
$this->payer_real_name = $this->encryptByRSA($realName);
if (empty($amount) || $amount < 0) throw new PayException('企业付款到银行卡金额错误');
$this->amount = bcmul($amount, 100, 0); //微信使用的单位为分,此处进行转化
}
}
<?php
namespace Payment\Common\Weixin\Data\Query;
use Payment\Common\PayException;
use Payment\Common\Weixin\Data\WxBaseData;
use Payment\Utils\ArrayUtil;
/**
* 微信企业付款到银行卡查询,当前微信仅支持根据商户订单号来进行查询
*
* @property string $trans_no 企业付款单号
*
* Class PayBankQueryData
* @package Payment\Common\Weixin\Data\Query
*/
class PayBankQueryData extends WxBaseData
{
protected function buildData()
{
$this->retData = [
'mch_id' => $this->mchId,
'partner_trade_no' => $this->trans_no,
'nonce_str' => $this->nonceStr,
];
$this->retData = ArrayUtil::paraFilter($this->retData);
}
protected function checkDataParam()
{
$transNo = $this->trans_no;
if (empty($transNo)) throw new PayException('请提供商户调用企企业付款到银行卡API时所使用的付款单号');
}
}
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
namespace Payment\Common\Weixin\Data; namespace Payment\Common\Weixin\Data;
use Payment\Common\BaseData; use Payment\Common\BaseData;
use Payment\Common\PayException;
/** /**
* Class BaseData * Class BaseData
...@@ -21,6 +22,7 @@ use Payment\Common\BaseData; ...@@ -21,6 +22,7 @@ use Payment\Common\BaseData;
* @property string $md5Key 用于加密的md5Key * @property string $md5Key 用于加密的md5Key
* @property string $appCertPem 从apiclient_cert.p12中导出证书部分的文件,为pem格式, * @property string $appCertPem 从apiclient_cert.p12中导出证书部分的文件,为pem格式,
* @property string $appKeyPem 从apiclient_key.pem中导出密钥部分的文件,为pem格式 * @property string $appKeyPem 从apiclient_key.pem中导出密钥部分的文件,为pem格式
* @property string $publicKeyPem 从获取RSA加密公钥API中得到的RSA公钥并进行将PKCS#1转为PKCS#8的pem格式文件
* @property string $tradeType 支付类型 * @property string $tradeType 支付类型
* @property string $terminal_id 终端设备号(门店号或收银设备ID),默认请传"WEB" * @property string $terminal_id 终端设备号(门店号或收银设备ID),默认请传"WEB"
* *
...@@ -43,7 +45,6 @@ abstract class WxBaseData extends BaseData ...@@ -43,7 +45,6 @@ abstract class WxBaseData extends BaseData
break; break;
case 'HMAC-SHA256': case 'HMAC-SHA256':
$sign = base64_encode(hash_hmac('sha256', $signStr, $this->md5Key)); $sign = base64_encode(hash_hmac('sha256', $signStr, $this->md5Key));
break; break;
default: default:
$sign = ''; $sign = '';
...@@ -51,4 +52,21 @@ abstract class WxBaseData extends BaseData ...@@ -51,4 +52,21 @@ abstract class WxBaseData extends BaseData
return strtoupper($sign); return strtoupper($sign);
} }
/**
* RSA加密
* @param string $string 需加密字符串
* @return string 返回加密后字符串
* @throws PayException
* @createTime 2019/10/10 14:00
*/
protected function encryptByRSA(string $string)
{
$publicKeyPem = $this->publicKeyPem;
if (empty($publicKeyPem) || true !== file_exists($publicKeyPem)) throw new PayException('RSA加密公钥有误');
$encryptedBlock = '';
if (true !== openssl_public_encrypt($string, $encryptedBlock, file_get_contents($this->publicKeyPem), OPENSSL_PKCS1_OAEP_PADDING)) throw new PayException('RSA加密失败');
return base64_encode($encryptedBlock);
}
} }
<?php <?php
/** /**
* Created by PhpStorm. * Created by PhpStorm.
* *
* Date: 2017/4/26 * Date: 2017/4/26
* Time: 下午5:39 * Time: 下午5:39
*/ */
...@@ -28,7 +28,7 @@ class WechatHelper extends WxBaseData ...@@ -28,7 +28,7 @@ class WechatHelper extends WxBaseData
$curl = new Curl(); $curl = new Curl();
$responseTxt = $curl->set([ $responseTxt = $curl->set([
'CURLOPT_HEADER' => 0 'CURLOPT_HEADER' => 0
])->post($xml)->submit($url); ])->post($xml)->submit($url);
if ($responseTxt['error']) { if ($responseTxt['error']) {
...@@ -37,7 +37,7 @@ class WechatHelper extends WxBaseData ...@@ -37,7 +37,7 @@ class WechatHelper extends WxBaseData
// 格式化为数组 // 格式化为数组
$retData = DataParser::toArray($responseTxt['body']); $retData = DataParser::toArray($responseTxt['body']);
if ($retData['return_code'] != 'SUCCESS') { if ($retData['return_code'] != 'SUCCESS') {
throw new PayException('微信返回错误提示:' . $retData['return_msg']); throw new PayException('微信返回错误提示:' . ($retData['return_msg'] ?? ($retData['retmsg'] ?? 'unknown')));
} }
return $retData['sandbox_signkey']; return $retData['sandbox_signkey'];
...@@ -46,7 +46,7 @@ class WechatHelper extends WxBaseData ...@@ -46,7 +46,7 @@ class WechatHelper extends WxBaseData
protected function buildData() protected function buildData()
{ {
$this->retData = [ $this->retData = [
'mch_id' => $this->mchId, 'mch_id' => $this->mchId,
'nonce_str' => $this->nonceStr, 'nonce_str' => $this->nonceStr,
]; ];
} }
...@@ -55,4 +55,4 @@ class WechatHelper extends WxBaseData ...@@ -55,4 +55,4 @@ class WechatHelper extends WxBaseData
{ {
// TODO: Implement checkDataParam() method. // TODO: Implement checkDataParam() method.
} }
} }
\ No newline at end of file
<?php <?php
/** /**
* *
* @createTime: 2016-07-15 14:56 * @createTime: 2016-07-15 14:56
...@@ -15,6 +16,7 @@ use Payment\Utils\StrUtil; ...@@ -15,6 +16,7 @@ use Payment\Utils\StrUtil;
final class WxConfig extends ConfigInterface final class WxConfig extends ConfigInterface
{ {
// 微信分配的公众账号ID // 微信分配的公众账号ID
public $appId; public $appId;
...@@ -42,6 +44,12 @@ final class WxConfig extends ConfigInterface ...@@ -42,6 +44,12 @@ final class WxConfig extends ConfigInterface
// key文件路径或者内容 // key文件路径或者内容
public $appKeyPem; public $appKeyPem;
/**
* 从获取RSA加密公钥API中得到的RSA公钥并进行将PKCS#1转为PKCS#8的pem格式文件
* 用于将字段进行rsa加密并转base64密文,将密文传给微信侧相应字段,如付款接口(enc_bank_no/enc_true_name)
*/
public $publicKeyPem;
// 支付类型 // 支付类型
public $tradeType; public $tradeType;
...@@ -61,16 +69,22 @@ final class WxConfig extends ConfigInterface ...@@ -61,16 +69,22 @@ final class WxConfig extends ConfigInterface
// 查询退款url // 查询退款url
const REFUDN_QUERY_URL = 'https://api.mch.weixin.qq.com/{debug}/pay/refundquery'; const REFUDN_QUERY_URL = 'https://api.mch.weixin.qq.com/{debug}/pay/refundquery';
// 企业付款的查询 // 企业付款到零钱的查询
const TRANS_QUERY_URL = 'https://api.mch.weixin.qq.com/{debug}/mmpaymkttransfers/gettransferinfo'; const TRANS_QUERY_URL = 'https://api.mch.weixin.qq.com/{debug}/mmpaymkttransfers/gettransferinfo';
// 企业付款到银行卡的查询
const PAY_BANK_QUERY_URL = 'https://api.mch.weixin.qq.com/{debug}/mmpaysptrans/query_bank';
// 申请退款url // 申请退款url
const REFUND_URL = 'https://api.mch.weixin.qq.com/{debug}/secapi/pay/refund'; const REFUND_URL = 'https://api.mch.weixin.qq.com/{debug}/secapi/pay/refund';
// 企业付款 // 企业付款到零钱
const TRANSFERS_URL = 'https://api.mch.weixin.qq.com/{debug}/mmpaymkttransfers/promotion/transfers'; const TRANSFERS_URL = 'https://api.mch.weixin.qq.com/{debug}/mmpaymkttransfers/promotion/transfers';
//企业付款到银行卡
const PAY_BANK_URL = 'https://api.mch.weixin.qq.com/{debug}/mmpaysptrans/pay_bank';
// 关闭订单url 尚未接入 // 关闭订单url 尚未接入
const CLOSE_URL = 'https://api.mch.weixin.qq.com/{debug}/pay/closeorder'; const CLOSE_URL = 'https://api.mch.weixin.qq.com/{debug}/pay/closeorder';
...@@ -164,12 +178,15 @@ final class WxConfig extends ConfigInterface ...@@ -164,12 +178,15 @@ final class WxConfig extends ConfigInterface
} }
// 以下两个文件,如果是调用资金流向接口,必须提供 // 以下两个文件,如果是调用资金流向接口,必须提供
if (! empty($config['app_cert_pem'])) { if (!empty($config['app_cert_pem'])) {
$this->appCertPem = $config['app_cert_pem']; $this->appCertPem = $config['app_cert_pem'];
} }
if (! empty($config['app_key_pem'])) { if (!empty($config['app_key_pem'])) {
$this->appKeyPem = $config['app_key_pem']; $this->appKeyPem = $config['app_key_pem'];
} }
if (!empty($config['public_key_pem'])) {
$this->publicKeyPem = $config['public_key_pem'];
}
if (key_exists('sign_type', $config) && in_array($config['sign_type'], ['MD5', 'HMAC-SHA256'])) { if (key_exists('sign_type', $config) && in_array($config['sign_type'], ['MD5', 'HMAC-SHA256'])) {
$this->signType = $config['sign_type']; $this->signType = $config['sign_type'];
...@@ -189,4 +206,6 @@ final class WxConfig extends ConfigInterface ...@@ -189,4 +206,6 @@ final class WxConfig extends ConfigInterface
$this->useSandbox = false;// 不是沙箱模式 $this->useSandbox = false;// 不是沙箱模式
} }
} }
} }
...@@ -15,7 +15,7 @@ namespace Payment; ...@@ -15,7 +15,7 @@ namespace Payment;
final class Config final class Config
{ {
const VERSION = '3.2.0'; const VERSION = '3.2.1';
//======================= 账户类型 ======================// //======================= 账户类型 ======================//
const WECHAT_PAY = 'wechat'; const WECHAT_PAY = 'wechat';
...@@ -83,7 +83,9 @@ final class Config ...@@ -83,7 +83,9 @@ final class Config
const WX_RED = 'wx_red';// 红包 const WX_RED = 'wx_red';// 红包
const WX_TRANSFER = 'wx_transfer';// 转账 const WX_TRANSFER = 'wx_transfer'; //微信企业付款到零钱
const WX_PAY_BANK = 'wx_pay_bank'; //微信企业付款到银行卡
const APPLE_PAY = 'applepay_upacp';//apple支付 const APPLE_PAY = 'applepay_upacp';//apple支付
......
<?php
namespace Payment\Query\Wx;
use Exception;
use Payment\Common\PayException;
use Payment\Common\Weixin\Data\Query\PayBankQueryData;
use Payment\Common\Weixin\WxBaseStrategy;
use Payment\Common\WxConfig;
use Payment\Config;
use Payment\Utils\Curl;
use Payment\Utils\DataParser;
/**
* 微信企业付款到银行卡查询
* Class WxPayBankQuery
* @package Payment\Query
*/
class WxPayBankQuery extends WxBaseStrategy
{
public function getBuildDataClass()
{
return PayBankQueryData::class;
}
/**
* 使用证书方式进行查询
* @param string $xml
* @param string $url
* @return array
*
*/
protected function curlPost($xml, $url)
{
$curl = new Curl();
$responseTxt = $curl->set([
'CURLOPT_HEADER' => 0,
'CURLOPT_SSL_VERIFYHOST' => false,
'CURLOPT_SSLCERTTYPE' => 'PEM', //默认支持的证书的类型,可以注释
'CURLOPT_SSLCERT' => $this->config->appCertPem,
'CURLOPT_SSLKEY' => $this->config->appKeyPem,
'CURLOPT_CAINFO' => $this->config->cacertPath,
])->post($xml)->submit($url);
return $responseTxt;
}
/**
* 返回付款查询url
* @return string
*
*/
protected function getReqUrl()
{
return WxConfig::PAY_BANK_QUERY_URL;
}
/**
* 处理微信的返回值并返回给客户端
* @param array $data
* @return array|mixed
* @createTime 2019/10/10 14:59
*/
protected function retData(array $data)
{
if ($this->config->returnRaw) {
$data['channel'] = Config::WX_PAY_BANK;
return $data;
}
//请求失败,可能是网络原因
if ($data['return_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error' => $data['return_msg'],
'channel' => Config::WX_PAY_BANK,
];
}
//业务失败
if ($data['result_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error_code' => $ret['err_code'] ?? '',
'error' => $data['err_code_des'] ?? '',
'channel' => Config::WX_PAY_BANK,
];
}
//正确
return $this->createBackData($data);
}
/**
* 处理微信的返回值并返回给客户端
* @param array $data
* @return array
* @createTime 2019/10/10 14:58
*/
protected function createBackData(array $data)
{
$retData = [
'is_success' => 'T',
'response' => [
'trans_no' => $data['partner_trade_no'], //商户企业付款单号
'transaction_id' => $data['payment_no'], //微信企业付款单号(代付成功后,返回的内部业务单号)
'amount' => bcdiv($data['amount'], 100, 2), //代付金额,单位:分。为了保持一致性,故此转为元
'fee' => bcdiv($data['cmms_amt'], 100, 2), //手续费金额,单位:分。为了保持一致性,故此转为元
'status' => strtolower($data['status']),//代付单状态
'reason' => $data['reason'], //失败原因
'payer_bank_no_md5' => $data['bank_no_md5'], //收款用户银行卡号(MD5加密)
'payer_real_name_md5' => $data['true_name_md5'], //收款人真实姓名(MD5加密)
'create_date' => $data['create_time'] ?? '', //微信企业付款订单创建时间
'pay_success_date' => $data['pay_succ_time'] ?? '', //微信企业付款到银行卡付款成功时间(依赖银行的处理进度,可能出现延迟返回,甚至被银行退票的情况)
'desc' => $data['desc'] ?? '', //付款描述
'channel' => Config::WX_PAY_BANK,
],
];
return $retData;
}
/**
* @param array $data
*
* @return array|string
* @throws PayException
* @throws Exception
*/
public function handle(array $data)
{
$buildClass = $this->getBuildDataClass();
try {
$this->reqData = new $buildClass($this->config, $data);
} catch (PayException $e) {
throw $e;
}
$this->reqData->setSign();
$xml = DataParser::toXml($this->reqData->getData());
$ret = $this->sendReq($xml);
return $this->retData($ret);
}
}
...@@ -21,6 +21,7 @@ use Payment\Query\MiPay\MiQuery; ...@@ -21,6 +21,7 @@ use Payment\Query\MiPay\MiQuery;
use Payment\Query\Sw\SwChargeQuery; use Payment\Query\Sw\SwChargeQuery;
use Payment\Query\TLpay\TLQuery; use Payment\Query\TLpay\TLQuery;
use Payment\Query\Wx\WxChargeQuery; use Payment\Query\Wx\WxChargeQuery;
use Payment\Query\Wx\WxPayBankQuery;
use Payment\Query\Wx\WxRefundQuery; use Payment\Query\Wx\WxRefundQuery;
use Payment\Query\Wx\WxTransferQuery; use Payment\Query\Wx\WxTransferQuery;
...@@ -36,11 +37,11 @@ class QueryContext ...@@ -36,11 +37,11 @@ class QueryContext
/** /**
* 设置对应的查询渠道 * 设置对应的查询渠道
* @param string $channel 查询渠道 * @param string $channel 查询渠道
* - @see Config * - @param array $config 配置文件
*
* @param array $config 配置文件
* @throws PayException * @throws PayException
* *
* @see Config
*
*/ */
public function initQuery($channel, array $config) public function initQuery($channel, array $config)
{ {
...@@ -62,9 +63,12 @@ class QueryContext ...@@ -62,9 +63,12 @@ class QueryContext
case Config::WX_REFUND:// 微信退款订单查询 case Config::WX_REFUND:// 微信退款订单查询
$this->query = new WxRefundQuery($config); $this->query = new WxRefundQuery($config);
break; break;
case Config::WX_TRANSFER:// 微信转款订单查询 case Config::WX_TRANSFER: //微信企业付款到零钱订单查询
$this->query = new WxTransferQuery($config); $this->query = new WxTransferQuery($config);
break; break;
case Config::WX_PAY_BANK: //微信企业付款到银行卡订单查询
$this->query = new WxPayBankQuery($config);
break;
case Config::CMB_CHARGE:// 招商支付查询 case Config::CMB_CHARGE:// 招商支付查询
$this->query = new CmbChargeQuery($config); $this->query = new CmbChargeQuery($config);
...@@ -91,7 +95,7 @@ class QueryContext ...@@ -91,7 +95,7 @@ class QueryContext
break; break;
default: default:
throw new PayException('当前仅支持:ALI_CHARGE ALI_REFUND WX_CHARGE WX_REFUND WX_TRANSFER CMB_CHARGE CMB_REFUND TLPAY MI_QUERY SW_QUERY FU_CHARGE FU_REFUND'); throw new PayException('当前仅支持:ALI_CHARGE ALI_REFUND WX_CHARGE WX_REFUND WX_TRANSFER WX_PAY_BANK CMB_CHARGE CMB_REFUND TLPAY MI_QUERY SW_QUERY FU_CHARGE FU_REFUND');
} }
} catch (PayException $e) { } catch (PayException $e) {
throw $e; throw $e;
...@@ -114,7 +118,7 @@ class QueryContext ...@@ -114,7 +118,7 @@ class QueryContext
*/ */
public function query(array $data) public function query(array $data)
{ {
if (! $this->query instanceof BaseStrategy) { if (!$this->query instanceof BaseStrategy) {
throw new PayException('请检查初始化是否正确'); throw new PayException('请检查初始化是否正确');
} }
......
<?php
namespace Payment\Trans;
use Payment\Common\Weixin\Data\PayBankData;
use Payment\Common\Weixin\WxBaseStrategy;
use Payment\Common\WxConfig;
use Payment\Config;
use Payment\Utils\Curl;
/**
* 微信企业付款到银行卡接口
* Class WxPayBank
* @package Payment\Trans
*
*/
class WxPayBank extends WxBaseStrategy
{
public function getBuildDataClass()
{
return PayBankData::class;
}
/*
* 返回企业付款到银行卡的url
*/
protected function getReqUrl()
{
return WxConfig::PAY_BANK_URL;
}
/**
* 微信退款接口,需要用到相关加密文件及证书,需要重新进行curl的设置
* @param string $xml
* @param string $url
* @return array
*
*/
protected function curlPost($xml, $url)
{
$curl = new Curl();
$responseTxt = $curl->set([
'CURLOPT_HEADER' => 0,
'CURLOPT_SSL_VERIFYHOST' => false,
'CURLOPT_SSLCERTTYPE' => 'PEM', //默认支持的证书的类型,可以注释
'CURLOPT_SSLCERT' => $this->config->appCertPem,
'CURLOPT_SSLKEY' => $this->config->appKeyPem,
'CURLOPT_CAINFO' => $this->config->cacertPath,
])->post($xml)->submit($url);
return $responseTxt;
}
/**
* 转款的返回数据
* @param array $ret
* @return mixed
*/
protected function retData(array $ret)
{
if ($this->config->returnRaw) {
$ret['channel'] = Config::WX_PAY_BANK;
return $ret;
}
// 请求失败,可能是网络
if ($ret['return_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error' => $ret['return_msg'],
'channel' => Config::WX_PAY_BANK,
];
}
// 业务失败
if ($ret['result_code'] != 'SUCCESS') {
return $retData = [
'is_success' => 'F',
'error_code' => $ret['err_code'] ?? '',
'error' => $ret['err_code_des'] ?? '',
'channel' => Config::WX_PAY_BANK,
];
}
return $this->createBackData($ret);
}
/**
* 返回数据
* @param array $data
* @return array
* @createTime 2019/10/10 14:35
*/
protected function createBackData(array $data)
{
$retData = [
'is_success' => 'T',
'response' => [
'trans_no' => $data['partner_trade_no'], //商户企业付款单号
'transaction_id' => $data['payment_no'], //微信企业付款单号(代付成功后,返回的内部业务单号)
'amount' => bcdiv($data['amount'], 100, 2), //代付金额,单位:分。为了保持一致性,故此转为元
'fee' => bcdiv($data['cmms_amt'], 100, 2), //手续费金额,单位:分。为了保持一致性,故此转为元
'pay_date' => $data['payment_time'] ?? date('Y-m-d H:i:s'), //微信企业付款到银行卡接口无付款成功时间字段
'channel' => Config::WX_PAY_BANK,
],
];
return $retData;
}
/**
* 企业付款,不需要签名
* @param array $retData
* @return bool
*/
protected function verifySign(array $retData)
{
return true;
}
}
...@@ -10,6 +10,7 @@ namespace Payment; ...@@ -10,6 +10,7 @@ namespace Payment;
use Payment\Common\BaseStrategy; use Payment\Common\BaseStrategy;
use Payment\Common\PayException; use Payment\Common\PayException;
use Payment\Trans\AliTransfer; use Payment\Trans\AliTransfer;
use Payment\Trans\WxPayBank;
use Payment\Trans\WxTransfer; use Payment\Trans\WxTransfer;
class TransferContext class TransferContext
...@@ -39,8 +40,11 @@ class TransferContext ...@@ -39,8 +40,11 @@ class TransferContext
case Config::WX_TRANSFER: case Config::WX_TRANSFER:
$this->transfer = new WxTransfer($config); $this->transfer = new WxTransfer($config);
break; break;
case Config::WX_PAY_BANK:
$this->transfer = new WxPayBank($config);
break;
default: default:
throw new PayException('当前仅支持:ALI WEIXIN两个常量'); throw new PayException('当前仅支持:ALI_TRANSFER、WX_TRANSFER、WX_PAY_BANK');
} }
} catch (PayException $e) { } catch (PayException $e) {
throw $e; throw $e;
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!