paypal作为国际在线支付的一种比较常用的收款工具,在现在的国际电子商务中使用的非常多,这里将就paypal支付接口与企业自己的网上电子商务系统之间沟通作一详细描述。
常见的电子商务系统实现的流程如下:
客户在系统内下订单 -> 将订单的金额信息提交到paypal网站 -> 客户在paypal上付款 -> paypal将客户的付款完成信息发送给电子商务系统 -> 系统收到paypal信息后确定客户订单已经付款 -> 进行发货等后续流程。
paypal的文档和sdk地址:
https://github.com/paypal/PayPal-PHP-SDK
https://github.com/paypal/PayPal-PHP-SDK/wiki
示例代码:https://github.com/paypal/PayPal-PHP-SDK/tree/master/sample
支付流程:https://developer.paypal.com/docs/api/quickstart/payments/
这里为了避免官方sdk更新,特地保留了下文中对接的版本
废话不多上代码:
1.将sdk包解压到Thinkphp3.2的/Core/Library/Vendor/目录下,目录名称改为:PayPal
2.前台页面,提交订单用的 ,新建文件paypal.html,用于测试
<form action="/index.php?g=appapi&m=paypal" method="post">
uid:<input type="text" name="uid" value="28344" /><br>
token:<input type="text" name="token" value="636f9ee6d262" /><br>
商品id:<input type="text" name="changeid" value="1" /><br>
coin:<input type="text" name="coin" value="600" /><br>
金额:<input type="text" name="money" value="1.00" /><br>
<input type="submit" value="提交" />
</form>
3.新建控制器PaypalController.class.php,以下就是所有过程的实现
<?php
namespace Appapi\Controller;
use PayPal\Api\Payer;
use PayPal\Api\Item;
use PayPal\Api\ItemList;
use PayPal\Api\Details;
use PayPal\Api\Amount;
use PayPal\Api\Transaction;
use PayPal\Api\RedirectUrls;
use PayPal\Api\Payment;
use PayPal\Auth\OAuthTokenCredential;
use PayPal\Exception\PayPalConnectionException;
use PayPal\Rest\ApiContext;
use PayPal\Api\PaymentExecution;
use Common\Controller\HomebaseController;
vendor('PayPal.autoload'); //导入sdk包
define('ACCEPT_URL', "http://".I("server.HTTP_HOST")."/index.php?g=appapi&m=paypal&a=callback"); //回调地址 可自定义
class PaypalController extends HomebaseController {
const clientId = '************';//ID 沙箱 这里正式/沙箱环境可以进行切换
const clientSecret = '************';//秘钥 这里正式/沙箱环境可以进行切换
const accept_url = ACCEPT_URL;//回调地址
const Currency = 'USD';//币种 可选CNY
const error_log = 'PayPal-error.log';//错误日志
const success_log = 'PayPal-success.log';//成功日志
protected $PayPal;
public function __construct()
{
$this->PayPal = new ApiContext(
new OAuthTokenCredential(
self::clientId,
self::clientSecret
)
);
$this->PayPal->setConfig(
array(
//'mode' => 'live',
'mode' => 'sandbox', //运行环境,正式使用切换到live即可
'http.ConnectionTimeOut' => 30,
)
);
parent::__construct(); //重新执行父类的构造函数
}
/**
* @Notes:
* 创建系统订单
* @Interface Establish
* @Author: 133814250@qq.com MengShuai
* @Date: 2020/3/15 20:57
*/
public function index()
{
$uid=I("uid");
$token=I("token");
$changeid=I("changeid");
$coin=I("coin");
$money=I("money");
if(!$uid || !$token){
$rs['code']=1001;
$rs['msg']='身份未验证:uid or token on null!';
$this->ajaxReturn($rs);
}
if(!$changeid || (!is_numeric($changeid))){
$rs['code']=1002;
$rs['msg']='商品ID错误:changeid on null or changeid not numeric!';
$this->ajaxReturn($rs);
}
if(!$coin || !$money){
$rs['code']=1003;
$rs['msg']='商品信息错误:coin or money on null!';
$this->ajaxReturn($rs);
}
if(!$true_token = M('users')->where("id={$uid} and user_type='2'")->getField("token")) {
$rs['code']=1004;
$rs['msg']='身份验证失败:uid not null!';
$this->ajaxReturn($rs);
}
if($true_token != $token){
$rs['code']=1005;
$rs['msg']='身份验证失败:token not null!';
$this->ajaxReturn($rs);
}
if(!$change_info = M("charge_rules")->where("id='{$changeid}'")->find()) {
$rs['code']=1006;
$rs['msg']='商品信息错误:changeid not null!';
$this->ajaxReturn($rs);
}
if ($change_info['coin'] != $coin || $change_info['money'] != $money) {
$rs['code']=1007;
$rs['msg']='商品信息错误:coin or money untrue!';
$this->ajaxReturn($rs);
}
if(!$rate = $this->getExchangeRate()){ //取央行的实时汇率
$rate = '6.5'; //汇率
}
$usd = sprintf("%.2f",$money/$rate);
$orderid=$uid.'_'.date('YmdHis').rand(100,999);
$orderinfo=array(
"uid"=>$uid,
"touid"=>$uid,
"money"=>$money,
"coin"=>$coin,
"orderno"=>$orderid,
"coin_give"=>$change_info['give'],
"type"=>5,
"status"=>0,
"addtime"=>time(),
"rate"=>$rate,
"usd"=>$usd
);
M("users_charge")->create($orderinfo);
if(M("users_charge")->add($orderinfo)) {
$product = '在线购买: ' . $change_info['name']; //商品名称
$price = $usd; //转化为美元
$shipping = 0; //手续费
$description = '咻币充值'; //商品描述
$custom = md5(md5('paypal' . $orderid)); //自定义变量 签名加密验证
$this->pay($product, $price, $shipping, $description, $custom, $orderid);
exit;
}
$rs['code']=1008;
$rs['msg']='订单创建失败!:orderinfo untrue!';
$this->ajaxReturn($rs);
}
/**
* @Notes:
* 跳转paypal支付页面
* @Interface pay
* @Author: 133814250@qq.com MengShuai
* @Date: 2020/3/15 20:59
* @param $product
* @param $price
* @param int $shipping
* @param $description
* @param $custom
* @param $orderid
*/
public function pay($product, $price, $shipping = 0, $description, $custom, $orderid)
{
$paypal = $this->PayPal;
$total = $price + $shipping;//总价
$payer = new Payer();
$payer->setPaymentMethod('paypal');
$item = new Item();
$item->setName($product)->setCurrency(self::Currency)->setQuantity(1)->setPrice($price);
$itemList = new ItemList();
$itemList->setItems([$item]);
$details = new Details();
$details->setShipping($shipping)->setSubtotal($price);
$amount = new Amount();
$amount->setCurrency(self::Currency)->setTotal($total)->setDetails($details);
$transaction = new Transaction();
$transaction->setAmount($amount)->setItemList($itemList)->setDescription($description)->setCustom($custom)->setInvoiceNumber($orderid);
$redirectUrls = new RedirectUrls();
$redirectUrls->setReturnUrl(self::accept_url . '&success=true')->setCancelUrl(self::accept_url . '&success=false');
$payment = new Payment();
$payment->setIntent('sale')->setPayer($payer)->setRedirectUrls($redirectUrls)->setTransactions([$transaction]);
try {
$payment->create($paypal);
} catch (PayPalConnectionException $e) {
echo $e->getData();
die();
}
$approvalUrl = $payment->getApprovalLink();
header("Location: {$approvalUrl}");
}
/**
* @Notes:
* 异步回调和业务处理
* @Interface Callback
* @Author: 133814250@qq.com MengShuai
* @Date: 2020/3/15 21:00
*/
public function Callback()
{
$success = trim($_GET['success']);
if ($success == 'false' && !isset($_GET['paymentId']) && !isset($_GET['PayerID'])) {
pay_logs(self::error_log, '取消付款');
$this->assign("reason",'取消付款!');
$this->display(':error');
exit;
}
$paymentId = trim($_GET['paymentId']);
$PayerID = trim($_GET['PayerID']);
if (!$success || !$paymentId || !$PayerID) {
pay_logs(self::error_log, '参数不全id'.$this->PayPal);
$this->assign("reason",'参数不全!');
$this->display(':error');
exit;
}
if (!isset($success, $paymentId, $PayerID)) {
pay_logs(self::error_log, '参数格式错误,支付失败-1');
$this->assign("reason",'参数格式错误,支付失败-1');
$this->display(':error');
exit;
}
if ((bool)$_GET['success'] === 'false') {
pay_logs(self::error_log, '订单未完成支付!支付失败-2。支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】');
$this->assign("reason",'订单未完成支付!支付失败-2。支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】');
$this->display(':error');
exit;
}
$payment = Payment::get($paymentId, $this->PayPal);
$execute = new PaymentExecution();
$execute->setPayerId($PayerID);
try {
$payment->execute($execute, $this->PayPal);
} catch (Exception $e) {
pay_logs(self::error_log, $e . '订单处理时发生错误,支付失败-3。支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】');
$this->assign("reason",'订单处理时发生错误,支付失败-3。支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】');
$this->display(':error');
exit;
}
$state = $payment->transactions[0]->related_resources[0]->sale->state; //页面回调状态
$approval = $payment->state; //订单支付状态
$invoice_number = $payment->transactions[0]->invoice_number; //商户订单号
$custom = $payment->transactions[0]->custom; //加密签名
$email = $payment->payer->payer_info->email; //付款人邮箱
if ($state == 'completed' && $approval == 'approved') {
if(md5(md5('paypal' . $invoice_number)) == $custom) {
if($order_info = M('users_charge')->where("orderno = '{$invoice_number}'")->order("id DESC")->find()) {
if($order_info['status'] != '0'){
pay_logs(self::error_log, '订单状态异常,订单号【' . $invoice_number . '】');
$this->assign("reason",'订单状态异常!:order status of error!');
$this->display(':error');
exit;
}
$order_data['trade_no'] = $email;
$order_data['status'] = '1';
if(M('users_charge')->where("orderno = '{$invoice_number}'")->save($order_data)) {
/**
* 业务处理
*/
pay_logs(self::success_log, '支付成功,订单号【' . $invoice_number . '】');
$this->assign("reason",'支付成功,订单号【' . $invoice_number . '】');
$this->display(':error');
exit;
}
pay_logs(self::error_log, '订单处理异常,订单号【' . $invoice_number . '】');
$this->assign("reason",'订单处理异常!:order save error!');
$this->display(':error');
exit;
}else{
pay_logs(self::error_log, '订单不存在,订单号【' . $invoice_number . '】');
$this->assign("reason",'订单不存在!:order is null!');
$this->display(':error');
exit;
}
}else{
pay_logs(self::error_log, '订单处理签名错误,订单号【' . $invoice_number . '】');
$this->assign("reason",'订单处理签名错误!:sign not auth!');
$this->display(':error');
exit;
}
}else{
pay_logs(self::error_log, $e . ',订单状态未认证,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】');
$this->assign("reason",'订单状态未认证!:state or approval not auth!');
$this->display(':error');
exit;
}
}
/**
* @Notes:
* 获取美元实时汇率
* @Interface getExchangeRate
* @Author: 133814250@qq.com MengShuai
* @Date: 2020/3/15 21:00
* @return array|bool|float
*/
public function getExchangeRate()
{
$date = date("Y-m-d", time());
//获得页面代码
$data = file_get_contents("http://srh.bankofchina.com/search/whpj/search.jsp?erectDate=".$date."¬hing=".$date."&pjname=1316&page=1");
//去掉非字符
$data = str_replace(array(" ","\r","\n","\t"), "", $data);
//得到汇率代码
preg_match('/<tr>[\s]*<td>美元<\/td>[\s]*<td>[\s|\S]*<\/td>[\s]*<\/tr>/',$data, $converted);
//开始各种调整格式,为了整理为数组
$data = str_replace("</tr><tr>", ";", $converted[0]);
$data = str_replace(array("<tr>","</tr>"), "", $data);
$data = str_replace("</td><td>", ",", $data);
$data = str_replace(array("<td>","</td>"), "", $data);
$rateList = explode(";", $data);
$rate = explode(",", $rateList[0]);
// var_dump($rate);
//$rate [0] 国家 [1] 现汇买入价 [2]现钞买入价[3]现汇卖出价[4]现钞卖出价[5]外管局中间价[6]中行折算价
$rate = $rate[3];
$rate = round(($rate/100),4);
if(is_numeric($rate))return $rate;
else return false;
}
}
无论从事什么行业,只要做好两件事就够了,一个是你的专业、一个是你的人品,专业决定了你的存在,人品决定了你的人脉,剩下的就是坚持,用善良專業和真诚赢取更多的信任。