以前事情比较繁忙,压根都没有时间去整理最近的工作。

最近稍微轻松点,就把自己在公司处理的支付业务拿出来,留个纪念,顺道回顾下以前自己支付的知识。

俗话说实践是检验整理的唯一标准,东西做的是否能用,只能去实际检验。

公司业务量可能是小点,去年9月初上线的东西,到现在充值也就是,我现查吧。。。。。。。。。。。。

这种级别的充值量,可能确实小了点,不过最起码说明,代码处理的业务逻辑,基本上没有什么大的问题。

行了,废话不说了,直接看以下介绍吧。

支付的逻辑需要三步:

(1)去合作的第三方拿到  商家号和秘钥 。

(2)自己项目中集成第三方的SDK,完成支付。

(3)第三方的异步通知,自己接收通知,修改订单状态。

知道RSA加密的可以忽略:

秘钥:RSA加密,需要生成,公钥publicKey 和私钥originKey

规则: 私钥加密,公钥解密。 知道双方的公钥和私钥,就互相验证了。

代码逻辑 【加密方式自己查看SDK,我们用的是RSA加密,所以就以它为例子】:

(1)页面写好提交的方法,然后准备提交。

//js获取页面数据,然后submit  建议:最好表单去提交
function sub(){
var pay_money = $("#pay_money").val();
var bank_code = $("input[name='bank']:checked").val();
$.ajax({
url:'/recharge/pay_data',
type:'post',
data:{'pay_money':pay_money,'bank_code':bank_code},
dataType:'json',
async: false,
success:function(e){
document.getElementById('order_no_number').value= e.order_no;
if(e.sign !='' && e.sign_type !=''){
//js动态创建表单,然后提交
var form = $("<form method='post' id='myform'></form>");
form.attr({"action":"跳转地址"});
form.attr({"target":"_blank"});
for (arg in e)
{
var input = $("<input type='hidden'>");
input.attr({"name":arg});
input.val(e[arg]);
form.append(input);
}
$("#form_hidden").append(form);
$("#myform").submit();
}
}
})
}

(2)服务端,得按照SDK所需参数,配置好各种必须的参数,提供页面数据提交的参数。

 public function pay_data(){
//银行代码和充值金额
$bank_code = $_POST['bank_code'];
$pay_money = $_POST['pay_money']; //充值数据
$uid = $this->session->userdata('uid');
if(!empty($uid) && !empty($bank_code) && !empty($pay_money)){
$hkd = $pay_money; //港币(测试完成,放开)
$hk_rate = $this->comm_func->getHkRate();//汇率,港币兑人民币
$order_amount = $pay_money * $hk_rate; //人民币 = 港币 * 汇率
$data['merchant_code'] = $this->config->item('merchant_code'); //配置中获取-商户号
$data['service_type'] = 'direct_pay';
$data['interface_version'] = 'V3.0';
$data['input_charset'] = 'UTF-8';
$data['notify_url'] = $this->config->item('web_notify_url'); //配置中获取-异步通知地址
$data['return_url'] = $this->config->item('web_return_url'); //配置中获取-支付完成后的跳转地址
$data['client_ip'] = $_SERVER["REMOTE_ADDR"];
$data['order_no'] = self::get_orderno($uid);
$data['order_time'] = date('Y-m-d H:i:s');
$data['order_amount'] = sprintf('%01.2f',$order_amount);
$data['product_name'] = '港币充值testpay';
$data['bank_code'] = $bank_code;
$sign_type ="RSA-S";
$sign_str = $this->doSign($data,$this->config->item('origPriKey'));//配置中获取-RSA加密的私钥
$pay_type = 1;
//插入订单记录
$sql = "INSERT INTO tb_pay (`uid`,`order_no`,`order_time`,`order_amount`,`hkd`,`rate`,`pay_type`,`bank_code`) VALUES('{$uid}','{$data['order_no']}','{$data['order_time']}','{$data['order_amount']}','{$hkd}','{$hk_rate}','{$pay_type}','{$data['bank_code']}')";
try {
$this->db->query($sql);
if ($this->db->affected_rows() <=0) {
self::write_log('sql未起效:'.$sql,'web_pay_err');
}
} catch (Exception $e) {
self::write_log('sql报错:'.$e,'web_pay_err');
}
//支付请求日志
self::write_log('返回数据:uid='.$uid.' data='.json_encode($data)."sign_str=".$sign_str,'web_get_paydata');
$data['sign'] = $sign_str;
$data['sign_type'] = $sign_type;
echo json_encode($data);
}
}

(3)服务端,处理异步通知--这个是重点,做好各种处理措施。

/*
* 服务器通知notify_url
*/
public function notify_url(){
$notifyStr = json_encode($_POST);
self::write_log($notifyStr,'web_notify_pay');//记录日志
$signArr = $_POST;
unset($signArr['sign'],$signArr['sign_type']);
$dinpaySign = base64_decode($_POST["sign"]);
$sign = $this->CheckSign($signArr,$dinpaySign,$this->config->item('PublicPriKey')); //获取验证的公钥 if(isset($_POST['order_no']) && isset($_POST['sign']) && $sign)
{ $DB_LOCK = $this->load->database('lock_tb', TRUE);
$order_no = isset($_POST['order_no'])?$_POST['order_no']:'';
$trade_no = isset($_POST['trade_no'])?$_POST['trade_no']:'';
$trade_time = isset($_POST['trade_time'])?$_POST['trade_time']:'';
$trade_status = isset($_POST['trade_status'])?$_POST['trade_status']:'';
//$bank_code = isset($_POST['bank_code'])?$_POST['bank_code']:'';
$bank_seq_no = isset($_POST['bank_seq_no'])?$_POST['bank_seq_no']:'';
$trade_amount = isset($_POST['order_amount'])?$_POST['order_amount']:''; //-----开启事物处理
$DB_LOCK->trans_start();
//mysql行级锁,处理完成,释放锁
$query = $DB_LOCK->query("SELECT * FROM `tb_pay` WHERE order_no = '{$_POST['order_no']}' FOR UPDATE");
$row = $query->row_array();
if (isset($row) && !empty($row))
{
//有且还未返回状态 更新
if (isset($row['trade_status']) && $row['trade_status']==0)
{
$trade_status_field = ($trade_status == 'SUCCESS')?1:2;
$upSql = "UPDATE tb_pay SET `trade_time` = '{$trade_time}',`trade_no` = '{$trade_no}',`trade_amount` = '{$trade_amount}',`trade_status`='{$trade_status_field}',`bank_seq_no`='{$bank_seq_no}',`back_time`='".date('Y-m-d H:i:s')."' WHERE `order_no` = '{$order_no}'"; try {
$DB_LOCK->query($upSql); if ($DB_LOCK->affected_rows() <=0) {
self::write_log('sql未起效:'.$upSql,'web_pay_err');
}else{
$this->update_money($row['uid'],$row['hkd']);
}
} catch (Exception $e) {
self::write_log('sql报错:'.$e,'web_pay_err');
}
}
//已有返回状态
if (isset($row['trade_status']) && $row['trade_status']!=0)
{
$trade_status_field = ($trade_status == 'SUCCESS')?1:2;
if ($trade_status_field == $row['trade_status'] && $trade_amount == $row['trade_amount'])
{
//重复发送
self::write_log('重复异步通知:'.$notifyStr,'web_pay_err');
}else {
//两次数据不一致
self::write_log('重要bug:与已有数据冲突:'.$notifyStr,'web_pay_err');
}
}
}else {
//无此记录
self::write_log('订单库中无此记录:'.$notifyStr,'web_pay_err');
}
$DB_LOCK->trans_complete();
//----事物结束,处理失败,回滚业务

echo 'SUCCESS'; //没有问题,打印success,处理成功
}else {
//验签失败
self::write_log('验签失败:'.$notifyStr.'sign:'.$sign,'web_pay_err');
}
}

代码所用的一些方法:

     /*生成订单号
* @uid
*/
public static function get_orderno($id)
{
$rand = date('ymd').substr(time(),-5).substr(microtime(),2,7);
$fix = substr(md5($id),0,7);
return $rand.$fix;
} /**
* sign
* */
public function doSign($inA,$origPriKey)
{
if(empty($inA))
return false;
$signStr = $this->doSort($inA);
$priKey = openssl_get_privatekey($origPriKey);
openssl_sign($signStr,$sign_info,$priKey,OPENSSL_ALGO_MD5);
$sign = base64_encode($sign_info);
return $sign;
}
/*
* sort
*/
private function doSort($param)
{
$signStr = '';
ksort($param);
foreach($param as $k => $v){
$signStr .= $k."=".$v."&";
}
$signStr = rtrim($signStr,'&');
self::write_log("web_send_signStr::".$signStr,'web_send_pay_signStr');
return $signStr;
} /*
* 验证notify中的签名
*/
public function CheckSign($inA,$dinpaySign,$pubKey){
if(empty($inA))
return false;
$param = $inA;
$signStr = '';
ksort($param);
foreach($param as $k => $v){
$signStr .= $k."=".$v."&";
}
$signStr = rtrim($signStr,'&'); $priKey = openssl_get_publickey($pubKey);
$sign = openssl_verify($signStr,$dinpaySign,$priKey,OPENSSL_ALGO_MD5);
if($sign){
return true;
}else{
return false;
}
}

PHP 第三方支付的更多相关文章

  1. 对接第三方支付接口-获取http中的返回参数

    这几天对接第三方支付接口,在回调通知里获取返回参数,有一家返回的json格式,请求参数可以从标准输入流中获取. //1.解析参数 , 读取请求内容 BufferedReader br; String ...

  2. 第三方支付过程中session失效问题

    第三方支付过程中session失效问题 时间 2015-05-13 12:36:23  IT社区推荐资讯 原文  http://itindex.net/detail/53436-session-问题 ...

  3. 第三方支付设计——自有账户支付

    笔者在上一篇blog<<第三方支付架构设计之-帐户体系>>中已经稍微全面的阐述了第三方支付架构设计中的账户体系,在该体系中,其实涉及了各种各样的账户:银行侧账户(包括用户在银行 ...

  4. 第三方支付设计——账户体系

    第三方支付架构设计之-帐户体系 一,      什么是第三方支付?         什么是第三方支付?相信很多人对这个名字很熟悉,不管是从各种媒体等都经常听到,可以说是耳熟能熟.但,如果非得给这个名词 ...

  5. python -django 之第三方支付

    神魔是第三方支付: 第三方支付是指具有一定实力和信誉保障的第三方独立机构.通过与各大银行签订合同,建立连接用户和银行支付结算系统的平台,从而实现电子支付模式.从另一个角度来看,第三方支付就是非金融机构 ...

  6. iOS第三方支付集成

    支付宝(alipay)和微信支付(Wechat Pay) 支付宝: 一.总体流程 (1)先与支付宝签约.获得商户ID(partner)和账号ID(seller)(注冊app⽤用) (2)下载对应的公钥 ...

  7. IOS 第三方支付的使用:支付宝

     本文转载至 http://blog.csdn.net/u014011807/article/details/47726799 总结一下支付宝iOS使用步骤: 1 第三方支付:支付宝 使用过程: 1. ...

  8. Android中集成第三方支付

    常见的第三方支付解决方案 支付宝支付 微信支付 银联支付 Ping++统一支付平台(需要继承服务器端和客户端) 短信支付 支付宝的集成流程 相关资料链接: 支付宝支付指引流程:支付指引流程 支付宝An ...

  9. php编写刷网课自助下单系统(第三方支付实例)

    此项目是由于本人刚刚入门php且在校代刷网课而编写的,由于在上课时间不方便接单,故特意写一个自助下单系统来实现客户自助下单.本项目主要实现以下功能:1.用户下单2.用户支付3.用户通过账号查询订单4. ...

  10. Paypal、Stripe、Braintree,跨境电商金流第三方支付该用哪家?

    在台湾做跨境电子商务生意,电商网站的金流肯定是一个最大的麻烦,Paypal或是Stripe和Braintree则是国际上大家最常用的金流整合第三方支付服务商.这些金流服务大幅简化网站付费过程,都让消费 ...

随机推荐

  1. windows程序设计 加载位图图片

    现在网上随便下个jpg图片,用windows自带的画图工具打开,点击画图工具左上角,文件->另存为->选择bmp,点击保存,保存好后,就得到一张位图了. 得到的位图,位图的内存比原图片jp ...

  2. C++引用和const引用、常量指针、指针常量

    1.引用.常量引用 引用主要被用做函数的形式参数--通常将类对象传递给一个函数. 引用在内部存放的是一个对象的地址,它是该对象的别名.引用不占用内存,因为取地址引用的值和被引用变量的地址相同.但是ob ...

  3. DataTable插件 后台分页 (服务器端分页)

    <script type="text/javascript">        var persontable; var personQueryCondition = { ...

  4. 传值与传引用(C++)

    reference(引用) 是C++对C的一个扩充 int a; int &b = a;//声明b是一个整形的引用变量 C语言,函数的参数传递有2种形式:传值方式调用和传引用方式调用 传值方式 ...

  5. Linux 组管理、权限

    权限说明 1. 组涉及到两个配置文件,组文件/etc/group,组密码管理员/etc/gshadow/,GID500往后的算普通组. 2.主组与附属组,当创建一个用户,没有制定,用户会默认创建一个与 ...

  6. 4. Dubbo原理解析-代理之接口定义 (转)

    转载自  斩秋的专栏  http://blog.csdn.net/quhongwei_zhanqiu/article/details/41577159 一:ProxyFactory的接口定义 impo ...

  7. 剑指offer(3)从尾到头打印链表

    题目描述 输入一个链表,从尾到头打印链表每个节点的值. 题目分析 比较简单,主要注意下从尾到头,可以用栈可以用递归,我给出我比较喜欢的代码吧 代码 /* function ListNode(x){ t ...

  8. 2017年天梯赛LV2题目汇总小结

    Ⅰ.L2-021 点赞狂魔---STL应用 微博上有个"点赞"功能,你可以为你喜欢的博文点个赞表示支持.每篇博文都有一些刻画其特性的标签,而你点赞的博文的类型,也间接刻画了你的特性 ...

  9. (转载)Unity_3DText穿透场景物体解决

    在unity的3D物体下有一个3DText 物体.这个物体可以在空间坐标中显示文本文字. 如下图: 这个3D Text在场景中的显示会出现穿透3D物体的现象.如图:本来这个hello world 的文 ...

  10. Learning-Python【26】:反射及内置方法

    反射的概念 可以用字符串的方式去访问对象的属性,调用对象的方法(但是不能去访问方法),Python 中一切皆对象,都可以使用反射. 反射有四种方法: hasattr:hasattr(object, n ...