1、安装依赖包
composer require paypal/rest-api-sdk-php
2、Paypal支付
<?php
namespace app\index\controller;
use think\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\Api\Refund;
use PayPal\Api\Sale;
use PayPal\Auth\OAuthTokenCredential;
use PayPal\Exception\PayPalConnectionException;
use PayPal\Rest\ApiContext;
use PayPal\Api\PaymentExecution;
//沙盒登录 https://www.sandbox.paypal.com/c2/signin
//参考文档 https://learnku.com/articles/26282
//paypal依赖包 composer require paypal/rest-api-sdk-php
class Paypal extends Controller
{
const clientId = 'xxxxxxxxxxxxxxxxxx';//应用ID
const clientSecret = 'xxxxxxxxxxxxxx';//应用秘钥
const accept_url = 'Paypal/Callback'; //同步回调地址
const error_log = './paypal_errors.log'; //错误日志
const Currency = 'USD';//币种
protected $PayPal;
public function __construct()
{
$this->PayPal = new ApiContext(
new OAuthTokenCredential(
self::clientId,
self::clientSecret
)
);
//如果是沙盒测试环境不设置,请注释掉
$this->PayPal->setConfig(
array(
'mode' => 'live',
)
);
}
/**
* paypal支付
* @param $price float 金额
* @param $order_sn string 订单号
* @param $product string 商品名
* @param $description string 商品描述
* @param $shipping float 运费
*
*/
public function pay($price=10, $order_sn=123456, $product='商品', $description='描述内容', $shipping = 0)
{
$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)->setInvoiceNumber(uniqid());
$redirectUrls = new RedirectUrls();
$redirectUrls->setReturnUrl(self::accept_url . '?success=true&order_sn='.$order_sn)->setCancelUrl(self::accept_url . '/?success=false');
$payment = new Payment();
$payment->setIntent('sale')->setPayer($payer)->setRedirectUrls($redirectUrls)->setTransactions([$transaction]);
try {
$payment->create($paypal);
$approvalUrl = $payment->getApprovalLink();
echo json_encode(['code'=>200, 'msg'=>'成功!','url'=>$approvalUrl]);
//header("Location: {$approvalUrl}"); //自动跳转页面
exit();
} catch (PayPalConnectionException $e) {
error_log(date('Y-m-d H:i:s')." paypal pay fail: ".$e->getData()."\n", 3, self::error_log);
echo json_encode(['code'=>202, 'msg'=>$e->getData()]);
exit();
}
}
}
3、同步回调
/**
* 同步回调方法
*/
public function Callback()
{
$success = trim($_GET['success']);
if ($success == 'false' && !isset($_GET['paymentId']) && !isset($_GET['PayerID'])) {
error_log(date('Y-m-d H:i:s')." paypal Callback fail: 取消付款"."\n", 3, self::error_log);
echo json_encode(['code'=>202, 'msg'=>'取消付款']);
exit();
}
$paymentId = trim($_GET['paymentId']);
$PayerID = trim($_GET['PayerID']);
$order_sn = trim($_GET['order_sn']); //订单号
if (!isset($success, $paymentId, $PayerID)) {
error_log(date('Y-m-d H:i:s')." paypal Callback fail: 支付失败"."\n", 3, self::error_log);
echo json_encode(['code'=>202, 'msg'=>'支付失败']);
exit();
}
if ((bool)$_GET['success'] === 'false') {
error_log(date('Y-m-d H:i:s')." paypal Callback fail:".'支付失败,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】'."\n", 3, self::error_log);
echo json_encode(['code'=>202,'msg'=>'支付失败,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】']);
exit();
}
$payment = Payment::get($paymentId, $this->PayPal);
$execute = new PaymentExecution();
$execute->setPayerId($PayerID);
try {
$payment->execute($execute, $this->PayPal);
// error_log(date('Y-m-d H:i:s')."同步回调:".json_encode($_GET)."\n", 3, self::error_log);
/*
* 将$paymentId保存到数据表
* */
echo json_encode(['code'=>200,'msg'=>'支付成功,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】,订单号【'.$order_sn.'】']);
exit();
} catch (Exception $e) {
error_log(date('Y-m-d H:i:s')." paypal Callback fail:".$e->getMessage(). ",支付失败,支付ID【" . $paymentId . "】,支付人ID【" . $PayerID . "】"."\n", 3, self::error_log);
echo json_encode(['code'=>202,'msg'=>$e . ',支付失败,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】']);
exit();
}
}
4、异步回调
public function notify(){ //异步回调(地址在paypal控制台设置)
//通知结果文档 https://developer.paypal.com/docs/api-basics/notifications/webhooks/notification-messages/
$json_data = file_get_contents('php://input');
// error_log(date('Y-m-d H:i:s')."异步回调:".$json_data."\n", 3, self::error_log);
$json_data = str_replace("'", '', $json_data);
$json_data = json_decode($json_data,true);
if(empty($json_data)){
error_log(date('Y-m-d H:i:s')."paypal notify fail: 参加为空!"."\n", 3, self::error_log);
exit();
}
try {
//自己打印$json_data的值看有那些是你业务上用到的
$id = $json_data['resource']['id']; //付款id(退款用),存表
$paymentId = $json_data['resource']['parent_payment']; //用此字段查询出订单表
if($json_data['resource_type']=='sale'){ //支付完成
/*
* 处理相关业务
* */
}elseif ($json_data['resource_type']=='refund'){ //退款成功
$total=$json_data['resource']['amount']['total']; //退款金额
/*
* 处理相关业务
* */
}
} catch (\Exception $e) {
//记录错误日志
error_log(date('Y-m-d H:i:s')."paypal notify fail:".$e->getMessage()."\n", 3, self::error_log);
exit();
}
return "success";
}
5、退款
public function returnMoney($txn_id = "3LV45487XU194815F",$price=1){ //异步回调sale通知中拿到的id
try {
$amt = new Amount();
$amt->setCurrency('USD')
->setTotal($price); // 退款的费用
$refund = new Refund();
$refund->setAmount($amt);
$sale = new Sale();
$sale->setId($txn_id);
$refundedSale = $sale->refund($refund, $this->PayPal);
} catch (\Exception $e) {
// PayPal无效退款
// return json_decode(json_encode(['message' => $e->getMessage(), 'code' => $e->getCode(), 'state' => $e->getMessage()])); // to object
echo json_encode(['code' => 202,'msg'=> $e->getMessage()]);
exit();
}
$refundedSale = $refundedSale->toArray();
// 退款完成
if($refundedSale['state']=='completed'){
echo json_encode(['code' => 200,'msg' =>'退款成功!']);
exit();
}else{
echo json_encode(['code' => 202,'msg' =>'退款失败!']);
exit();
}
}