Refund using PHP PayPal REST API?

余生长醉 提交于 2020-07-07 19:41:10

问题


I am working on a PHP application that integrates into PayPal's REST API. I have the transactions processing correctly and saving the transaction ID into a MySQL database. I am now trying to refund the sale but cannot get it to cease giving a "Incoming JSON request does not map to API request" error. Does anyone have any advice on how to get this working?

I left some of my attempts in the code with comments about their result. I'm feeling a bit lost in the direction to get this working. Any help will be appreciated.

I've been following the documentation located here: https://developer.paypal.com/docs/integration/direct/payments/refund-payment/

I may be struggling adapting that request to PHP.

I found this bit of code: https://github.com/paypal/PayPal-PHP-SDK/blob/master/sample/payments/RefundCapture.php

But it is 'requiring' a tree of files that seem a bit excessive for a simple refund request.

<?php

require_once(dirname(__FILE__) . '/paypalSDK/autoload.php'); // require paypal files
require_once(dirname(__FILE__) . '/paypalSDK/paypal/rest-api-sdk-php/sample/common.php'); // require paypal files
// call required stuff
use PayPal\Api\Address;
use PayPal\Api\Amount;
use PayPal\Api\Details;
use PayPal\Api\ExecutePayment;
use PayPal\Api\FundingInstrument;
use PayPal\Api\Item;
use PayPal\Api\ItemList;
use PayPal\Api\Payer;
use PayPal\Api\Payment;
use PayPal\Api\PaymentCard;
use PayPal\Api\PaymentExecution;
use PayPal\Api\Transaction;
use PayPal\Api\Capture;
use PayPal\Api\Refund;
use PayPal\Api\RefundRequest;
use PayPal\Api\Sale;

$orderID = $_GET['order_id']; // order ID we wish to refund
// pull the details from the database so we have the transaction refund ID
$refQ = mysql_query("SELECT * FROM orders WHERE orders_ID = $orderID", $db);
$refC = mysql_num_rows($refQ);
if(!empty($refC)){

    $refR = mysql_fetch_assoc($refQ);
    $refNumber = $refR['orders_transaction_ref']; // paypal transaction ID
    $FinalTotal = $refR['orders_order_total']; // order total
    // paypal credentials
    $apiContext = new \PayPal\Rest\ApiContext(
        new \PayPal\Auth\OAuthTokenCredential(
            'abc',    
            'xyz'  
        )
    );
    $apiContext->setConfig(array('mode' => 'live',)); // live use, not sandbox

    //$amount = new Amount();  // commenting these out seemingly has no effect
    //$amount->setCurrency("USD")  // commenting these out seemingly has no effect
    //    ->setTotal($FinalTotal);  // commenting these out seemingly has no effect

    $refund = new Refund(); // if I comment these 2 lines out, I get "refund cannot be null"
    $refund->setAmount($FinalTotal); // if I comment these 2 lines out, I get "refund cannot be null"

    $sale = new Capture(); // if I comment these 2 lines out, I get "Call to a member function refund() on a non-object"
    $sale->setId($refNumber); // if I comment these 2 lines out, I get "Call to a member function refund() on a non-object"

    $refundRequest = new RefundRequest(); // commenting these out seemingly has no effect
    $refundRequest->setAmount($FinalTotal); // commenting these out seemingly has no effect

    //$captureRefund = $sale->refundCapturedPayment($refundRequest, $apiContext); // failure, error 400

    try {
        $sale->refund($refund, $apiContext);
        //$sale->refundCapturedPayment($refund, $apiContext); // failure, error 400
    } catch (Exception $ex) {
        echo '<pre style="font-size:16px;">';
            var_dump($ex);
        echo '</pre>';
        exit();
        die;
    }
}

Update 3/8/2017 @ 12:23pm

I have seemingly made 'some' progress, though I use the term loosely. The state is returning as 'completed' but the refund is not actually happening. It also isn't returning the parent payment ID like the samples demonstrate. Any ideas on this? I'm still trying to get this to work.

This is the result I am presently getting from PayPal:

object(PayPal\Api\Refund)#3 (1) {
  ["_propMap":"PayPal\Common\PayPalModel":private]=>
  array(5) {
    ["id"]=>
    string(17) "123456789123456789"
    ["create_time"]=>
    string(20) "2017-03-06T20:56:32Z"
    ["state"]=>
    string(9) "completed"
    ["amount"]=>
    object(PayPal\Api\Amount)#9 (1) {
      ["_propMap":"PayPal\Common\PayPalModel":private]=>
      array(3) {
        ["total"]=>
        string(4) "0.02"
        ["currency"]=>
        string(3) "USD"
        ["details"]=>
        object(PayPal\Api\Details)#13 (1) {
          ["_propMap":"PayPal\Common\PayPalModel":private]=>
          array(1) {
            ["subtotal"]=>
            string(4) "0.02"
          }
        }
      }
    }
    ["links"]=>
    array(1) {
      [0]=>
      object(PayPal\Api\Links)#17 (1) {
        ["_propMap":"PayPal\Common\PayPalModel":private]=>
        array(3) {
          ["href"]=>
          string(59) "https://api.paypal.com/v1/payments/refund/123456789123456789"
          ["rel"]=>
          string(4) "self"
          ["method"]=>
          string(3) "GET"
        }
      }
    }
  }
}

Below is the code presently returning the response above...

require_once(dirname(__FILE__) . '/paypalSDK/autoload.php'); // require paypal files
require_once(dirname(__FILE__) . '/paypalSDK/paypal/rest-api-sdk-php/sample/bootstrap.php'); // require paypal files
require_once(dirname(__FILE__) . '/paypalSDK/paypal/rest-api-sdk-php/sample/common.php'); // require paypal files
// call required stuff
use PayPal\Api\Address;
use PayPal\Api\Amount;
use PayPal\Api\Details;
use PayPal\Api\ExecutePayment;
use PayPal\Api\FundingInstrument;
use PayPal\Api\Item;
use PayPal\Api\ItemList;
use PayPal\Api\Payer;
use PayPal\Api\Payment;
use PayPal\Api\PaymentCard;
use PayPal\Api\PaymentExecution;
use PayPal\Api\Transaction;
use PayPal\Api\Capture;
use PayPal\Api\Refund;
use PayPal\Api\RefundRequest;
use PayPal\Api\Sale;

$orderID = $_GET['order_id']; // internal order ID we wish to refund

// pull the details from database so we have the PayPal transction ID
$refQ = mysql_query("SELECT * FROM orders WHERE orders_ID = $orderID", $db);
$refC = mysql_num_rows($refQ);
if(!empty($refC)){

    $refR = mysql_fetch_assoc($refQ);
    $refNumber = $refR['orders_transaction_ref']; // PayPal transaction ID 

    // attempted 'PAY-' ID, results in "Requested resource ID was not found."
    // $refNumber = $refR['orders_payment_ref']; // ex. PAY-123456789012345678901234 

    $FinalTotal = $refR['orders_order_total']; // order total

    // paypal credentials
    $apiContext = new \PayPal\Rest\ApiContext(
        new \PayPal\Auth\OAuthTokenCredential(
            'abc',    
            'xyz'  
        )
    );
    $apiContext->setConfig(array('mode' => 'live',)); // live use, not sandbox

    //$amount = new Amount();  // commenting these out seemingly has no effect
    //$amount->setCurrency("USD")  // commenting these out seemingly has no effect
    //    ->setTotal($FinalTotal);  // commenting these out seemingly has no effect

    /*
    // attempted code 
    // ### Refund object
    $refund = new Refund();
    //$refund = new RefundRequest();
    $refund->setAmount($FinalTotal);
    */

    $refund = new Refund();
    $refund->setId($refNumber);
    $refund->setSaleId($refNumber);
    $refund->setAmount($FinalTotal);
    $refund->setReason("Testing refund code");
    //$refund->get($refNumber, $apiContext);

    // Attempted code, didn't work
    // https://stackoverflow.com/questions/18927591/paypal-api-how-to-get-sale-id-and-refund-payment-made-via-paypal
    // Fatal error: Uncaught exception 'InvalidArgumentException' with message 'paymentId cannot be null' in /home/cartridgeworkspl/public_html/cfx_controllers/paypalSDK/paypal/rest-api-sdk-php/lib/PayPal/Validation/ArgumentValidator.php:25 Stack trace: #0 
    // $payments = Payment::get($refNumber, $apiContext);
    // $payments->getTransactions();
    // $obj = $payments->toJSON();//I wanted to look into the object
    // $paypal_obj = json_decode($obj);//I wanted to look into the object
    // $transaction_id = $paypal_obj->transactions[0]->related_resources[0]->sale->id;
    // $this->refund($transaction_id);//Call your custom refund method

    try {
        //var_dump($refund);
        $refundResponse = Refund::get($refNumber, $apiContext);
        echo '<pre>';
            var_dump($refundResponse);
        echo '</pre>';
        echo $refundResponse->getState();
        echo $refundResponse->getReasonCode();
    } catch (Exception $ex) {
        // NOTE: PLEASE DO NOT USE RESULTPRINTER CLASS IN YOUR ORIGINAL CODE. FOR SAMPLE ONLY
        ResultPrinter::printError("Get Payment", "Payment", null, null, $ex);
        exit(1);
    }

}

Any help would be supremely appreciated.


回答1:


Documentation: https://developer.paypal.com/docs/api/quickstart/refund-payment/#set-the-refund-object

To make payment and refund use: paypal/rest-api-sdk-php

First, when you making payment, it must be as "sale".

[...]
$payment = new Payment();
$payment->setIntent('sale')
    ->setPayer($payer)
    ->setRedirectUrls($redirect_urls)
    ->setTransactions(array($transaction));
[...]

PayPal will return payment with "paymentId" value, and you need to save it.

Refund example.

//Previosly saved payments ID.
$paymentId = 'PI_Fv54vfd45';

//Create API connection
$apiContext = new ApiContext(new OAuthTokenCredential($paypalConf['client_id'], $paypalConf['secret']));
$apiContext->setConfig($paypalConf['settings']);

//Get sale from payment
$paypalPayment = new Payment();
$paymentInfo = $paypalPayment->get($paymentId, $apiContext);

$transactions = $paymentInfo->getTransactions();
if(empty($transactions[0])){
    return false;
}

$relatedResources = $transactions[0]->getRelatedResources();
if(empty($relatedResources[0])){
    return false;
}
$sale = $relatedResources[0]->getSale();

$refund = new Refund();
$amt = (new Amount())->setTotal(100)->setCurrency('EUR');
$refund->setAmount($amt);
$refund->setReason('Sale refund');

$refundedSale = $sale->refund($refund, $apiContext);
return ($refundedSale->getState() === 'completed');



回答2:


I believe the issue is that when you submit the refund, you must also set the API context. The first context establishes connection to the API, then you set the sale ID. I believe you then must set the API context for that sale ID, but I'm not 100%.




回答3:


I managed to work my way through it. Below is code that is successfully refunding PayPal orders made through the REST API.

<?php
    require_once(dirname(__FILE__) . '/paypalSDK/autoload.php'); // require paypal files
    require_once(dirname(__FILE__) . '/paypalSDK/paypal/rest-api-sdk-php/sample/bootstrap.php'); // require paypal files
    require_once(dirname(__FILE__) . '/paypalSDK/paypal/rest-api-sdk-php/sample/common.php'); // require paypal files

    // call required PayPal functionality
    use PayPal\Api\Address;
    use PayPal\Api\Amount;
    use PayPal\Api\Details;
    use PayPal\Api\ExecutePayment;
    use PayPal\Api\FundingInstrument;
    use PayPal\Api\Item;
    use PayPal\Api\ItemList;
    use PayPal\Api\Payer;
    use PayPal\Api\Payment;
    use PayPal\Api\PaymentCard;
    use PayPal\Api\PaymentExecution;
    use PayPal\Api\Transaction;
    use PayPal\Api\Capture;
    use PayPal\Api\Refund;
    use PayPal\Api\RefundRequest;
    use PayPal\Api\Sale;

    $orderID = $_GET['order_id']; // internal order ID we wish to refund

    // pull the details from database so we have the PayPal transction ID
    $refQ = mysql_query("SELECT * FROM orders WHERE orders_ID = $orderID", $db);
    $refC = mysql_num_rows($refQ);
    if(!empty($refC)){

        $refR = mysql_fetch_assoc($refQ); // array of order data

        $refNumber = $refR['orders_transaction_ref']; // PayPal transaction ID 
        $FinalTotal = $refR['orders_order_total']; // order total

        // get PayPal access token via cURL
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, "https://api.paypal.com/v1/oauth2/token");
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'Accept: application/json',
            'Accept-Language: en_US'
        ));
        curl_setopt($ch, CURLOPT_USERPWD, 'USER:PASS');
        curl_setopt($ch, CURLOPT_POSTFIELDS, 'grant_type=client_credentials');
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
        $output = curl_exec($ch);
        $json = json_decode($output);
        $token = $json->access_token; // this is our PayPal access token

        // refund PayPal sale via cURL
        $header = Array(
            "Content-Type: application/json",
            "Authorization: Bearer $token",
        );
        $ch = curl_init("https://api.paypal.com/v1/payments/sale/$refNumber/refund");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, '{}');
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        $res = json_decode(curl_exec($ch));
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        // if res has a state, retrieve it
        if(isset($res->state)){
            $state = $res->state;
        }else{
            $state = NULL; // otherwise, set to NULL
        }

        // if we have a state in the response...
        if($state == 'completed'){
            // the refund was successful
        }else{
            // the refund failed
            $errorName = $res->name; // ex. 'Transaction Refused.'
            $errorReason = $res->message; // ex. 'The requested transaction has already been fully refunded.'
        }
    }
?>


来源:https://stackoverflow.com/questions/42588772/refund-using-php-paypal-rest-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!