基本介绍
用途广泛,很多网站都继承了在线支付功能,如paypal,网银在线,易宝支付,支付宝,快钱等第三方平台.
原理
基本上,每个银行都有自家的支付接口,为什么不直接连接到银行的接口去支付,而需要通过第三方支付?
- 银行众多,每家的银行用的技术不一样,比如招行使用
.net
,农行使用java
。 - 提供的支付地址和支付的接口参数不同.
- 如果直接提供银行接口,则需要针对不同银行开发不同的支付程序,而且要与银行分别签订合同.
第3方支付平台充当了一个什么角色?
给用户提供一个统一的支付接口
.
e.g 网银在线的chinabank.
作为网站开发方,只需要和chinabank签订协议,支付时,只需要针对chinabank提供的接口来操作即可.
支付过程
提交支付(本站) ---> 第三方支付平台 --> 银行站点 --> 第三方支付平台 --> 本站
网银 和 银行 之间并不需要去做处理.
支付过程:
先POST发送给 第三方支付平台 信息, 如:订单号,总金额,商户号
返回过程:
第三方支付平台POST给本站 信息
如:支付结果,支付金额...
使用到的知识点
提交表单 和 接收表单
支付过程的问题
- 如何确认表单是否正确
- 如何确认金额是否被篡改
所有的加密平台都是通过:密钥 + 加密 的方式来防伪.
作为开发者,需要完成的事情是:防伪
和 处理表单
防伪
确认数据的合法:
利用md5加密的不可逆性,通过特殊的密钥,双方来对比md5加密后的字符串,密钥只有开发者和第三方平台知晓.
方式:
- 数字证书
- md5(密钥+字段)
**支付表单处理**
总金额: 123
订单号: 234
...
...
在发送如上信息的同时,再把`字段拼接+密钥 通过md5()` [那些字段拼接,看第三方文档要求]
同时把md5()的计算结果一起发送到平台.
支付处理表单
提交
/********** 在线支付 md5加密 **********/
// 订单总金额 币种 订单编号 商户编号 回调地址 密钥
// v_amonut v_moneytype v_oid v_mid v_url key // 中间不允许有空格
$v_amonut = $total;
$v_moneytype = 'CNY';
$v_oid = $order_sn;
$v_mid = '1009001'; // 20272562
$v_url = 'http://www.bool.com/recive.php'; // 回调url
$key = '#(%#WU)(UFGDKJGNDFG'; // 密钥
$v_md5info = strtoupper(md5($v_amonut . $v_moneytype . $v_oid . $v_mid . $v_url . $key));
/********** 在线支付 md5加密 **********/
返回
/*
Array (
[v_oid] => OI20161211743801
[v_pstatus] => 20
[v_pstring] => 支付完成
[v_pmode] => 工商银行
[v_md5str] => 658B07BD92E0F3D259C8EED69982856D
[v_amount] => 42
[v_moneytype] => CNY
[remark1] =>
[remark2] =>
)
*/
// v_oid v_pstatus v_amount v_moneytype key 组成
$v_oid = $_POST['v_oid'];
$v_pstatus = $_POST['v_pstatus'];
$v_amount = $_POST['v_amount'];
$v_moneytype = $_POST['v_moneytype'];
$key = '#(%#WU)(UFGDKJGNDFG';
$v_md5str = $_POST['v_md5str'];
$md5info = strtoupper( md5($v_oid . $v_pstatus . $v_amount . $v_moneytype . $key) );
// 对比 计算后的 md5info 和 表单中的 v_md5str
if ( $md5info != $v_md5str ) { // 支付失败
$msg = $_POST['v_pstring'];
exit;
}
// 支付成功
// 执行SQL语句,把订单号 对应的订单改为 已支付
echo $_POST['v_oid'];
注意:
- 接口文档中,有一处不可照用 -- 即支付接口地址为
http://192.162.2.199/pay/index.php
- 回调地址用IP来写,不能用localhost, e用IP.
- 确保别人用的IP能够访问到你的机器
在线支付常遇到的问题
什么是"掉单"? 怎么引起的?
指在银行网站在线支付成功后,钱已经从银行账户中扣除,但是,在购物网站上,相应的订单并没有转成已支付状态,或者充值时,账户余额没有发生变化。
操作方式:
每隔3-4分钟重新发送一次,直到收到ok为止.
网站确认成功页面,依然是接收数据,操作订单。如果成功,返回ok,否则,返回error.
如果是一笔充值业务,虽然无法伪造POST数据发送给payres.php。但是,如果先充值50,并通过控制台监测对payres.php的发送信息。然后照抄此次的POST的内容,然后恶意提交多次,因此此次的POST的md5info是正确的,导致多次充值。
在充值前,先形成一个充值订单,
比如:cz201206120987 0
第一次提交: 0 ---> 1, money = money + 50
当再次提交,1 ---> 1, money = money + 50
....
操作方式:
需要在update之前,先判断订单的状态,是否为已完成支付,如果是已完成,直接提示退出.
其它支付平台
所有的第3方支付平台,都有如下关键点:
- 支付网关地址,其实就是把支付信息提供给该地址.
- 商户都有一个密钥,并且,此密钥和订单信息配置,生成一个加密串(该加密串赋值检验合法性)。
- 支付平台支付成功后,会往网站的某个回调URL发送数据。(回调URL接收数据并根据队则,生成检验串,并判断合法性)。
不同处:
- 表单提交的表单项不同
- 拼凑表单字段时,规则不同
- 再伸出加密串时的函数,未必是md5加密.
PHP加密
MD5信息加密算法
md5
加密属于单向加密
语法:string md5(string $str [,bool $raw_output = false])
: 计算str的md5散列值
$str: 原始字符串
$raw_output: 可选的raw_output被设置为true,那么md5报文摘要将以16字节长度的原始二进制格式返回。返回以32位字符十六进制数字形式返回散列值 (不常用)
Crypt加密
Crypt
加密属于单向加密
语法:string crypt(string $str[,string $salt])
返回一个基于UNIX DES
算法或系统上其它可用的代替算法的散列字符串.
$str: 需要的机密的明文
$salt: 加密时的干扰串,是编码更安全 (盐分)标准的字符2位
注意:如果加密时没有加上这个$salt参数,将随机生成一个干扰串,否则刷新加密密文不变。
Sha1加密
Sha1
加密属于单向加密
语法:string sha1(string $str[,bool $raw_output = false])
: 计算字符串的sha1散列值
$str: 加密的字符串
$raw_output: 如果可选的raw_output参数被设置为true,那么sha1摘要将以20字符长度的原始格式返回,否则返回值是一个40字符长度的十六进制数字。
返回sha1散列值字符串。
URL编码加密
urlencode(string $str): 编码URL字符串
$str: 要编码的字符串
返回值: 返回编码后的字符串
urldecode(string $str):解码已编码的URL字符串
返回值:返回解码后的字符串
编码规范:此字符串中除了-_.
之外的所有非字母数字字符都将被替换成百分号(%)后面跟两位十六进制,空格则编码为加号(+)
rawurlencode(sting $str): 按照RFC1738对URL进行编码
$str:要编码的URL
返回值:返回字符串,把空格编码为%20
rawurldecode(string $str): 对已编码的URL字符串进行编码
$str: 要解码的URL
返回值:返回字符串,此字符串中百分号%后跟两位十六进制的序列都将别替换成原义字符
Base64
数据编码的作用.
base64_encode(string $data): 使用base64对data进行编码
$data: 要编码的数据
base64_decode(string $data[, bool $strict=false]): 对使用MIME base64编码的数据进行解码
$strict: 如果输入的数据超出了base64字母表,则返回false
信息加密技术
-
单项散列加密
单项散列加密是指通过对不同输入长度的信息进行散列计算,得到固定长度的输出,这个是散列计算过程是单向的,即不能对固定长度的输出进行计算从而获得输入信息。
-
对称散列加密
对称加密是指加密和解密使用的密钥是同一个密钥或者可以互相推算。
-
非对称散列加密
密钥特定的公开
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。