この記事では主に PHP UnionPay オンライン決済インターフェースの開発例を紹介し、皆様のお役に立てれば幸いです。
1. UnionPay セルフサービス テスト プラットフォームにログインします (ログイン アドレス: open.unionpay.com)。次のように、[マイ 製品] をクリックします。たとえば、モバイル Web 決済 (WAP 決済) がその例です。
2. 以下に示すように、左側のメニューのテスト パラメーターをクリックして、テスト プロセスに必要なパラメーターを表示します。 テスト証明書をクリックし、2 つの証明書をダウンロードします。1 つはサフィックスが付いた秘密キー証明書です。 .pfx、もう 1 つはサフィックス .cer が付いた公開キー証明書です。ダウンロード後、秘密キー証明書ファイルの名前は acp_test_sign.pfx に変更されます。または、ダウンロードしない場合は、この例を直接使用することもできます。 TP3.2 の例では、すべての証明書ファイルが Public/cer にあります。
3. TP3.2 のサンプルには、テストに使用できる関連コードが含まれています。コード内にコメントがあります。開始する前に、環境の PHP バージョンが 5.3 に基づいていることを確認してください。curl および openssl 機能を有効にする必要があり、テストはオンラインで実行する必要があります。問題が発生した場合は、このフォルダーにある PHP バージョンの SDK を参照してください。これは、私がテストしたときは実行できませんでした。何が起こったのか分かりません。
4. 運用環境に切り替えて、次の問題に注意してください:
4.1 まず、受け取った販売者アクティベーション電子メールの指示に従って、Web サイト http://cs.cfca.com.cn/ にアクセスします。実稼働証明書ファイル:
[ダウンロード] をクリックした後、ダウンロード操作が完了すると、ダウンロードが成功したことを示すプロンプトがページに表示されます。ダウンロードした証明書は自動的に IE に保存されます。次のステップは証明書をエクスポートすることです。
4.2 証明書ファイルをエクスポートする: IE ブラウザを開き、右上隅にある歯車をクリックし、ツール = "インターネット オプション =" コンテンツ = "証明書を開きます。図に示すように:
証明書は、ダウンロードした証明書を見つけます。販売者のメールにマークされている名前で識別できます:
上の図で赤でマークされた名前は、ダウンロードしたものと同じである必要があります。
それを見つけて [エクスポート] をクリックします: 途中の次のステップでは、次の手順に注意する必要があります
上記のパスワードは、本番環境で設定するパスワードです。 config.php を 6 桁に設定してください (数字のみ。文字や記号は設定しないでください)
エクスポートした証明書のファイル名を指定し、名前を acp_prod_sign に設定し、証明書を保存するディレクトリを選択します。次に設定をデスクトップにエクスポートします。完了すると、デスクトップに acp_prod_sign.pfx ファイルが表示されます。これは、実稼働環境で使用される秘密キー ファイルです。証明書ディレクトリ /Public/cer にコピーします。次のステップでは、この証明書を販売者サービス Web サイトにアップロードします。
4.2 証明書をマーチャントサービス Web サイトにアップロードします。 https://merchant.unionpay.com/portal/login.jsp にログインします
エクスポートした acp_prod_sign.pfx ファイルをアップロードし、[アップロード] をクリックします。
次のステップでは、証明書を有効にし、[セキュリティ証明書管理] をクリックして有効にします。
次のステップでは、UnionPay 公開キーをダウンロードします
ファイルを解凍し、中にある 2 つの証明書を /Public/cer に置きます。次に、config.php に移動し、ファイルのコメントに従って実稼働環境に切り替えます。
TP3.2のコード情報は以下のとおりです:
/App/Home/Conf/config.php
<?phpreturn array( //'配置项'=>'配置值'
'UNIONPAY' => array(// 银联配置
//测试环境参数
'frontUrl' => 'https://101.231.204.80:5000/gateway/api/frontTransReq.do', //测试环境前台交易请求地址
//'frontUrl' => 'https://gateway.95516.com/gateway/api/frontTransReq.do', //生产环境前台交易请求地址
'singleQueryUrl' => 'https://101.231.204.80:5000/gateway/api/backTransReq.do', //测试环境单笔查询请求地址
//'singleQueryUrl' => 'https://gateway.95516.com/gateway/api/queryTrans.do', //生产环境单笔查询请求地址
'signCertPath' =>getcwd().'/Public/cer/acp_test_sign.pfx', //签名证书路径 这个证书就是你在https://open.unionpay.com/ajweb/account/testPara 上面下载的那个商户私钥证书 供你测试使用
//'signCertPath' =>getcwd().'/Public/cer/acp_prod_sign.pfx', //签名证书路径 这个证书就是你在 商户开通邮件里面叫你去 http://cs.cfca.com.cn/ 下载并把私钥上传至商户服务网站并启用的那个私钥文件
'signCertPwd' => '000000', //测试环境签名证书密码
//'signCertPwd' => '135246', //生产环境证书签名证书密码 这个密码是你在IE导出上述私钥文件时候你自己定义的6位数字密码
//'verifyCertPath' => getcwd().'/Public/cer/verify_sign_acp.cer', //测试环境验签证书路径
'verifyCertPath' => getcwd().'/Public/cer/acp_prod_verify_sign.cer', //验签证书路径
'merId' => '777290058138754', //测试商户代码
//'merId' => '8024400481****', //生产环境商户代码 从你的商户开通邮件里面有
), 'UNIONPAY_CONFIG'=>array(// 银联配置
'version' => '5.0.0', //版本号
'encoding' => 'GBK', //编码方式
'signMethod' => '01', //签名方式
'txnType' => '01', //交易类型
'txnSubType' => '01', //交易子类
'bizType' => '000201', //产品类型
'channelType' => '07',//渠道类型
'frontUrl' => "http://win2.qbt8.com/ase_admin/index.php/Home/ypay/pay_success", //前台通知地址
'backUrl' => "http://win2.qbt8.com/ase_admin/index.php/Home/ypay/notify", //后台通知地址
'frontFailUrl' => "http://win2.qbt8.com/ase_admin/index.php/Home/ypay/pay_fail", //失败交易前台跳转地址
'accessType' => '0', //接入类型
'merId' => '777290058138754', //测试商户代码
//'merId' => '8024400481*****', //生产环境商户代码
'txnTime' => date('YmdHis'), //订单发送时间
'currencyCode' => '156', //交易币种
),
);?>
ログイン後にコピー
コントローラーのコードは以下のとおりです:
<?php /*
|-------------------------------------------------------------|
| 银联在线支付控制器
|author:shuguang date:2016-11-16 |-------------------------------------------------------------|
*/
namespace Home\Controller;
use Think\Controller;
class YpayController extends Controller
{
/**
* 支付配置
* @var array
*/
public $config = array();
/**
* 支付参数,提交到银联对应接口的所有参数
* @var array
*/
public $params = array();
/**
* 自动提交表单模板
* @var string
*/
private $formTemplate = <<<HTML
<!DOCTYPE HTML>
<meta>
<title>支付</title>
<p>跳转中...</p>
ログイン後にコピー
<script>
document.onreadystatechange = function(){ if(document.readyState == "complete") {
document.pay_form.submit();
}
};
</script>
HTML;
public function index(){
//前台表单 $this->display();
}
/*支付成功后 前台通知地址*/
public function pay_success(){ echo "
支付成功!
";
}
/*失败交易前台跳转地址*/
public function pay_fail(){ echo "
支付失败!
";
}
/*生产支付参数 提交支付 */
function usespay(){ $this->config = C('UNIONPAY');//从配置里读取
$config = C('UNIONPAY_CONFIG'); $config['certId'] = $this->getSignCertId(); //证书ID $config['orderId'] = mt_rand(111111111,999999999);//订单号 自定义
$config['txnAmt'] = I("post.money")*100; //交易金额,单位分 $this->params = $config;
// $_SESSION['ceshi']=$config;
/* 以下是自己的业务逻辑操作 生产支付记录到本地数据库
$money = I("post.money");; $user_id = $this->user_id; $OrderId = $config['orderId'];//生成随机订单号 $pay_type = "银联";//支付方式 1余额 2支付宝 $pay_fee = M('handfee')->find(2); if ($pay_fee['type'] == 1){ $fee=$pay_fee['rate']*$money;
}else { $fee=$pay_fee['fee'];
}
//订单表数据 $order = array( "order_id"=>$OrderId, "uid"=>$user_id, "pay_mode"=>1, "pay_channels"=>2, "fee"=>$fee, "status"=>0,//待审核 "beizhu"=>"银联在线充值", "ent_money"=>$money-$fee, "time"=>time(), "sub_time"=>time(), "pay_money"=>$money, "pay_type"=>$pay_type,//1余额支付 2支付宝支付
//"type"=>2
);*/
//$Ord=M('pay');
//$Ord->add($order); $html = $this->createPostForm();//构建自动提交HTML表单 echo $html;
}
function ceshi(){
dump($_SESSION);
}
function usernotify(){// 付款后返回商家
}
function notify(){//后台通知路径
/*付款后业务逻辑代码 */ $orderId = $_POST ['orderId']; //其他字段也可用类似方式获取 $respCode = $_POST ['respCode']; //判断respCode=00或A6即可认为交易成功 if ($respCode=='00'||$respCode=='A6'){
/*通过写入文件的方式记录返回的订单号等 */ $str = "--------- ".date('Y-m-d H:i:s')." ---------"; $str .= "orderId:".$orderId."\r\n"; $str .= "respCode:".$respCode."\r\n"; $str .= "--------- END -----------"."\r\n";
file_put_contents('unionpay_notify_log.log', $str);
/* 以下是支付成功后的数据库操作 请根据需要自行操作
$order['status']=1; $order['check_time']=time();
M('pay')->where(array('order_id'=>$orderId))->save($order);
$order_info = M('pay')->where(array('order_id'=>$orderId))->find();
$log['user_id']=$order_info['uid']; $log['user_money']=$order_info['pay_money']; $log['change_time']=time(); $log['desc']="银联在线充值";
M('account_log')->add($log);
M('users')->where('user_id='.$order_info['uid'])->setInc('user_money',$order_info['ent_money']);
*/
}
}
function unionpayfail(){
}
/* function orderPay($orderinfo,$state){ $filename = 'Log/yapy';
file_put_contents($filename.'/'.$orderinfo['orderId'].'.txt', json_encode($_POST), FILE_APPEND);
//$order = D('order');
//$payment = D('payment');
//$where['order_sn'] = array('in', array($orderinfo['orderId']));
//$orinfo = $order->where($where)->find(); $rs = $payment->where($where)->find(); if (empty($rs) && $orinfo['order_state'] json_encode($where1))), true); $data1['order_state'] = (int) $state;
//$orderwhere['order_sn'] = array('in', array($orderinfo['orderId']));
//$order->where($orderwhere)->save($data1); if($orinfo['balance'] >0 && $orinfo['isblance'] == 1){ if($userinfo1[0]['balance']-$orinfo['balance']>=0){ $total1 = $total1-$data['balance']; $istrue = req_api("api_key", C("API_KEY"), C('USER_API') . "user/api/removeBalance/", array('user' =>session('id'),'count'=>$orinfo['balance'],'type'=>'d'));
//$this->BanlanceRecord(2,$orinfo['balance'],'购物消费',session('id'));
}
} if ($orinfo['jindou'] >0 && $orinfo['isjindou'] == 1) { if($userinfo1[0]['user_wealth']-$orinfo['jindou']>=0){ $istrue = req_api("api_key", C("API_KEY"), C('USER_API') . "user/api/AddJindou/", array('user' =>session('id'),'count'=>$orinfo['jindou'],'type'=>'d')); $this->ChangeRecord(2,$orinfo['jindou'],'购物抵消',session('id')); $total1 = $total1-($orinfo['jindou']/100);
}
} $data['order_sn'] = $orderinfo['orderId']; $data['buyer_id'] = $orderinfo['certId']; $data['buyer_user'] = '银联支付'; $data['is_success'] = 'T'; $data['notify_time'] = substr($orderinfo['txnTime'],0,4)."-".substr($orderinfo['txnTime'],4,2).'-'.substr($orderinfo['txnTime'],6,2).' '.substr($orderinfo['txnTime'],8,2).':'.substr($orderinfo['txnTime'],10,2).':'.substr($orderinfo['txnTime'],12,2); $data['trade_no'] = $orderinfo['queryId']; $data['seller_id'] = $orderinfo['merId']; $data['total_fee'] = $orderinfo['txnAmt']*100; $data['sign'] = $orderinfo['signature']; $data['user_id'] = $orinfo['user_id']; $data['order_state'] = (int) $state; $data['status'] = 0; $payment->data($data)->filter('strip_tags')->add();
} $record = A('Shop/Orderrecord'); $shuju['order_state'] = (string) $state; $shuju['action_user_id'] = session('id'); $shuju['action_descrption'] = $type.'支付宝付款' . $orinfo['payable_total']; $record->ChangeOrderRecords($orinfo['_id'], $shuju); $orderrecord = A('Shop/Order'); $orderrecord->CashMoneyRecord(2, $orinfo['payable_total'], '购物消费--订单(' . $orderinfo['out_trade_no'] . ')', session('id'));
layout(false); $this->assign('orderinfo', $orinfo); $this->display('Order:PaySuccess6');
} */
/**
* 构建自动提交HTML表单
* @return string
*/
public function createPostForm()
{ $this->params['signature'] = $this->sign(); $input = '';
foreach($this->params as $key => $item) { $input .= "\t\t
\n";
} return sprintf($this->formTemplate, $this->config['frontUrl'], $input);
}
/**
* 验证签名
* 验签规则:
* 除signature域之外的所有项目都必须参加验签
* 根据key值按照字典排序,然后用&拼接key=value形式待验签字符串;
* 然后对待验签字符串使用sha1算法做摘要;
* 用银联公钥对摘要和签名信息做验签操作
*
* @throws \Exception
* @return bool
*/
public function verifySign()
{ $publicKey = $this->getVerifyPublicKey(); $verifyArr = $this->filterBeforSign();
ksort($verifyArr); $verifyStr = $this->arrayToString($verifyArr); $verifySha1 = sha1($verifyStr); $signature = base64_decode($this->params['signature']); $result = openssl_verify($verifySha1, $signature, $publicKey); if($result === -1) {
// throw new \Exception('Verify Error:'.openssl_error_string()); echo 'Verify Error:'.openssl_error_string();
} return $result === 1 ? true : false;
}
/**
* 取签名证书ID(SN)
* @return string
*/
public function getSignCertId()
{
return $this->getCertIdPfx($this->config['signCertPath']);
}
/**
* 签名数据
* 签名规则:
* 除signature域之外的所有项目都必须参加签名
* 根据key值按照字典排序,然后用&拼接key=value形式待签名字符串;
* 然后对待签名字符串使用sha1算法做摘要;
* 用银联颁发的私钥对摘要做RSA签名操作
* 签名结果用base64编码后放在signature域
*
* @throws \InvalidArgumentException
* @return multitype|string
*/
private function sign() { $signData = $this->filterBeforSign();
ksort($signData); $signQueryString = $this->arrayToString($signData); if($this->params['signMethod'] == 01) {
//签名之前先用sha1处理
//echo $signQueryString;exit; $datasha1 = sha1($signQueryString); $signed = $this->rsaSign($datasha1);
} else {
//throw new \InvalidArgumentException('Nonsupport Sign Method'); echo 'Nonsupport Sign Method';
} return $signed;
}
/**
* 数组转换成字符串
* @param array $arr
* @return string
*/
private function arrayToString($arr)
{ $str = '';
foreach($arr as $key => $value) { $str .= $key.'='.$value.'&';
} return substr($str, 0, strlen($str) - 1);
}
/**
* 过滤待签名数据
* signature域不参加签名
*
* @return array
*/
private function filterBeforSign()
{ $tmp = $this->params; unset($tmp['signature']); return $tmp;
}
/**
* RSA签名数据,并base64编码
* @param string $data 待签名数据
* @return mixed
*/
private function rsaSign($data)
{ $privatekey = $this->getSignPrivateKey(); $result = openssl_sign($data, $signature, $privatekey); if($result) { return base64_encode($signature);
} return false;
}
/**
* 取.pfx格式证书ID(SN)
* @return string
*/
private function getCertIdPfx($path)
{ $data = fopen($path); $pkcs12certdata = file_get_contents($path);
openssl_pkcs12_read($pkcs12certdata, $certs, $this->config['signCertPwd']); $x509data = $certs['cert'];
openssl_x509_read($x509data); $certdata = openssl_x509_parse($x509data); return $certdata['serialNumber'];
}
/**
* 取.cer格式证书ID(SN)
* @return string
*/
private function getCertIdCer($path)
{ $x509data = file_get_contents($path);
openssl_x509_read($x509data); $certdata = openssl_x509_parse($x509data); return $certdata['serialNumber'];
}
/**
* 取签名证书私钥
* @return resource
*/
private function getSignPrivateKey()
{ $pkcs12 = file_get_contents($this->config['signCertPath']);
openssl_pkcs12_read($pkcs12, $certs, $this->config['signCertPwd']); return $certs['pkey'];
}
/**
* 取验证签名证书
* @throws \InvalidArgumentException
* @return string
*/
private function getVerifyPublicKey()
{
//先判断配置的验签证书是否银联返回指定的证书是否一致 if($this->getCertIdCer($this->config['verifyCertPath']) != $this->params['certId']) {
// throw new \InvalidArgumentException('Verify sign cert is incorrect'); echo 'Verify sign cert is incorrect';
} return file_get_contents($this->config['verifyCertPath']);
}
}
ビューファイルの内容は以下のとおりです:
nbsp;html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<meta>
<title>银联支付测试</title>
<h1>银联支付测试</h1>
ログイン後にコピー
証明書ファイルはここにアップロードできません。これはスクリーンショットです。自分でダウンロードできます:
関連推奨事項:
交通銀行オンラインバンキングオンライン支払いインターフェース PHP で実装された ECSHOP プラグインと使用例
以上がPHP銀聯オンライン決済インターフェースの開発例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。