Using a custom_id to match a PayPal transaction to an order OR getting the PayPal transaction id

whitedragon101
Contributor
Contributor

 

I have two goals :

 

1) Attach a custom_id to an outgoing javascript order and get it back in the onApprove response so I can know it is the same transaction.  (if this is not necessary and PayPal api security is enough then great).

2) To be able to match transactions between my database and the PayPal dashboard with certainty, I want to either :

      Get the PayPal transaction id (displayed on the invoice receipt shown in dashboard) back from the onApprove function so I can enter it into my database.

      OR

      Enter my own custom_id with the transaction that appears on the PayPal invoice receipt shown in dashboard. 

 

Below is my code using custom_id which does not work (i.e  details.purchase_units.custom_id is blank)

 

 

 

 

 

 

<script>
      
paypal.Buttons({
    createOrder: function(data, actions) {
       return actions.order.create({
          purchase_units: [{
            custom_id: '55',
            description: "Some description",
            amount: { value: ’20’}  
          }]
       });
    },
          
    onApprove: function(data, actions) {
       return actions.order.capture().then(function(details) {
	    console.log(details);
            console.log(data);
            alert("Transaction completed by order id =" + data.orderID+ " our id = " + details.purchase_units.custom_id);
            payPalConfirm(details.purchase_units.custom_id); //my ajax call to change my db entry to say payment was made                        
       });                       
    }
}).render('#paypal-button-container');
                        
</script>

 

 

 

 

 

 

Login to Me Too
1 ACCEPTED SOLUTION

Accepted Solutions
Solved

FixItDik
Contributor
Contributor

Hi, yes, I managed some of this but I don't think you get to see your own OrderID in the dashboard.

 

The first part is to pass your OrderID in the order data, the other is to log/store the PayPal OrderID in your database (as your OrderID does not get returned in some of the web hook calls). Here's the code on my payment page (the bits in curly brackets are filled in by the PHP code that generates the page), I will describe a bit more after it:

 

 

 

<html>
<head>
  <!-- my stuff -->
  <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Ensures optimal rendering on mobile devices. -->
  <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <!-- Optimal Internet Explorer compatibility -->
</head>
<body>
  <script
    src="https://www.paypal.com/sdk/js?client-id={MY_CLIENTID}&currency=GBP" data-order-id="{My Order ID}">
  </script>
  <!-- my stuff -->
  <!-- my 'please wait' message that is shown when PayPal confirms authorisation -->
  <div id="PleaseWait" style="display:none;position: fixed;top: 50%;left: 50%;width: 300px;line-height: 200px;height: 200px;margin-left: -150px;margin-top: -100px;background-color: #f1c40f;text-align: center;z-index: 10;font-size:40px;outline: 9999px solid rgba(0,0,0,0.5);">Please Wait</div>

  <div id="paypal-button-container"></div>
  <script>
    ppoid=pptid='';
    paypal.Buttons({
		createOrder: function(data, actions) {
			// This function sets up the details of the transaction, including the amount and line item details.
			return actions.order.create({
       			style: {
       				label: "pay"
       			},
				application_context: { 
				    brand_name: 'Our Club Name',
				    shipping_preference: 'NO_SHIPPING'
				},
				purchase_units: [{
					reference_id: '{My Order ID}',
					amount: {
						value: '{total to pay}',
						currency_code: 'GBP'
					}
				}],
				payer: {
					name: {
						given_name: '{FIRST}',
						surname: '{LAST}'
					},
					email_address: '{EMAIL}',

					address: {
						country_code: '{Country CODE}', 
						address_line_1: '{ADD1}',
						address_line_2: '{ADD2}',
						admin_area_2: '{TOWN}',
						admin_area_1: '{COUNTY}',
						postal_code: '{POSTCODE}'
					}
				}
			}).then(function(details) {
				ppoid = details;
				return details;
			});
		},
		// this function handles the successful outcome
		onApprove: function(data, actions) {
			x = document.getElementById('PleaseWait');
			if (x) {x.style.display='inherit';}
			return actions.order.capture().then(function(details) {
				pptid = details.purchase_units[0].payments.captures[0].id;
				setTimeout(function(){window.location.href='thankyou.php?oid={My Order ID}&ppoid=' + ppoid + '&pptid=' + pptid;}, 500);
				return details;
			});
		},
	    onError: (err) => {
	      console.error('*** error from the onError callback', err);
	    }
	}).render('#paypal-button-container');
    // This function displays Smart Payment Buttons on your web page.
  </script>
</body>

 

 

 

So you will see my "{My Order ID}" going in to the "purchase_units" section in the "reference_id" parameter:

 

 

...
				purchase_units: [{
					reference_id: '{My Order ID}',
					amount: {
...

 

 

You will also see that I added some code to the end of the "createOrder" method to store the PayPal orderID it generates in a local (JavaScript) variable "ppoid":

 

 

...
						postal_code: '{POSTCODE}'
					}
				}
			}).then(function(details) {
				ppoid = details;
				return details;
			});
...

 

 

And then on the "OnApprove" callback I redirect the user to my "thankyou.php" page passing it our OrderID, PayPal's OrderID as well as the "TransactionID" (returned when I call the "capture" method in the javascript):

 

 

...
		onApprove: function(data, actions) {
			x = document.getElementById('PleaseWait');
			if (x) {x.style.display='inherit';}
			return actions.order.capture().then(function(details) {
				pptid = details.purchase_units[0].payments.captures[0].id;
				setTimeout(function(){window.location.href='thankyou.php?oid={My Order ID}&ppoid=' + ppoid + '&pptid=' + pptid;}, 500);
				return details;
			});
...

 

(I display a "Please wait" message when PayPal hands control back to me as I found the capture method was taking a few seconds to complete giving the user the chance to click other buttons on the page and cause chaos)

 

My ThankYou page just reads my database to see the current status of my order - the webhook code will often have received the call from PayPal by the time it is doing this and the webook will have updated the order to show it has been completed successfully, but just in case the ThankYou page will check and either display a "Transaction complete, thank you" type message or a "We are just waiting on PayPal" message.  The webhook code also sends them an email so they know when the transaction completes. Let me know if you would like to see the webhook code.

 

I hope that helps!

 

Regards

 

Dik (aka FixItDik)

View solution in original post

Login to Me Too
7 REPLIES 7
Solved

FixItDik
Contributor
Contributor

Hi, yes, I managed some of this but I don't think you get to see your own OrderID in the dashboard.

 

The first part is to pass your OrderID in the order data, the other is to log/store the PayPal OrderID in your database (as your OrderID does not get returned in some of the web hook calls). Here's the code on my payment page (the bits in curly brackets are filled in by the PHP code that generates the page), I will describe a bit more after it:

 

 

 

<html>
<head>
  <!-- my stuff -->
  <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Ensures optimal rendering on mobile devices. -->
  <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <!-- Optimal Internet Explorer compatibility -->
</head>
<body>
  <script
    src="https://www.paypal.com/sdk/js?client-id={MY_CLIENTID}&currency=GBP" data-order-id="{My Order ID}">
  </script>
  <!-- my stuff -->
  <!-- my 'please wait' message that is shown when PayPal confirms authorisation -->
  <div id="PleaseWait" style="display:none;position: fixed;top: 50%;left: 50%;width: 300px;line-height: 200px;height: 200px;margin-left: -150px;margin-top: -100px;background-color: #f1c40f;text-align: center;z-index: 10;font-size:40px;outline: 9999px solid rgba(0,0,0,0.5);">Please Wait</div>

  <div id="paypal-button-container"></div>
  <script>
    ppoid=pptid='';
    paypal.Buttons({
		createOrder: function(data, actions) {
			// This function sets up the details of the transaction, including the amount and line item details.
			return actions.order.create({
       			style: {
       				label: "pay"
       			},
				application_context: { 
				    brand_name: 'Our Club Name',
				    shipping_preference: 'NO_SHIPPING'
				},
				purchase_units: [{
					reference_id: '{My Order ID}',
					amount: {
						value: '{total to pay}',
						currency_code: 'GBP'
					}
				}],
				payer: {
					name: {
						given_name: '{FIRST}',
						surname: '{LAST}'
					},
					email_address: '{EMAIL}',

					address: {
						country_code: '{Country CODE}', 
						address_line_1: '{ADD1}',
						address_line_2: '{ADD2}',
						admin_area_2: '{TOWN}',
						admin_area_1: '{COUNTY}',
						postal_code: '{POSTCODE}'
					}
				}
			}).then(function(details) {
				ppoid = details;
				return details;
			});
		},
		// this function handles the successful outcome
		onApprove: function(data, actions) {
			x = document.getElementById('PleaseWait');
			if (x) {x.style.display='inherit';}
			return actions.order.capture().then(function(details) {
				pptid = details.purchase_units[0].payments.captures[0].id;
				setTimeout(function(){window.location.href='thankyou.php?oid={My Order ID}&ppoid=' + ppoid + '&pptid=' + pptid;}, 500);
				return details;
			});
		},
	    onError: (err) => {
	      console.error('*** error from the onError callback', err);
	    }
	}).render('#paypal-button-container');
    // This function displays Smart Payment Buttons on your web page.
  </script>
</body>

 

 

 

So you will see my "{My Order ID}" going in to the "purchase_units" section in the "reference_id" parameter:

 

 

...
				purchase_units: [{
					reference_id: '{My Order ID}',
					amount: {
...

 

 

You will also see that I added some code to the end of the "createOrder" method to store the PayPal orderID it generates in a local (JavaScript) variable "ppoid":

 

 

...
						postal_code: '{POSTCODE}'
					}
				}
			}).then(function(details) {
				ppoid = details;
				return details;
			});
...

 

 

And then on the "OnApprove" callback I redirect the user to my "thankyou.php" page passing it our OrderID, PayPal's OrderID as well as the "TransactionID" (returned when I call the "capture" method in the javascript):

 

 

...
		onApprove: function(data, actions) {
			x = document.getElementById('PleaseWait');
			if (x) {x.style.display='inherit';}
			return actions.order.capture().then(function(details) {
				pptid = details.purchase_units[0].payments.captures[0].id;
				setTimeout(function(){window.location.href='thankyou.php?oid={My Order ID}&ppoid=' + ppoid + '&pptid=' + pptid;}, 500);
				return details;
			});
...

 

(I display a "Please wait" message when PayPal hands control back to me as I found the capture method was taking a few seconds to complete giving the user the chance to click other buttons on the page and cause chaos)

 

My ThankYou page just reads my database to see the current status of my order - the webhook code will often have received the call from PayPal by the time it is doing this and the webook will have updated the order to show it has been completed successfully, but just in case the ThankYou page will check and either display a "Transaction complete, thank you" type message or a "We are just waiting on PayPal" message.  The webhook code also sends them an email so they know when the transaction completes. Let me know if you would like to see the webhook code.

 

I hope that helps!

 

Regards

 

Dik (aka FixItDik)

Login to Me Too

whitedragon101
Contributor
Contributor

Thank you soooooo much for this. Absolute life saver.

Yes please I would definitely like to see the Webhook code 🙂 . I am also coding the server side in PHP

 

Just two questions 

 

1)

You put your order id into the transaction in the purchase units section using 'reference_id' but I also see you have it in the url call here

 src="https://www.paypal.com/sdk/js?client-id={MY_CLIENTID}&currency=GBP" data-order-id="{My Order ID}">

 What is 'data-order-id' doing and will this appear either in the returned Webhook data or on the invoice you get to see in the PayPal area?

 

2)

You get the ppoid here (which I assume is the PayPal order id).  But I don't see any Ajax call to save this to the database. Where do you save the PayPal id to the database?  Do you do this on the thankyou.php page? If you do, is it certain that thankyou.php will run before the Webhook runs or just likely?

 

.then(function(details) {
				ppoid = details;
				return details;
			});

 

 

 

Login to Me Too

FixItDik
Contributor
Contributor

Hiya,

 

Let me start with the questions:

1) This was kind of a belt and braces solution as I spotted this option was available, to be honest I think it is the entry in the orders object that results in the order id being set and this one in the script call is ignored but I could be wrong and it does no harm so I left it in.

 

2) there's no Ajax call to update the database in this code - I am simply storing it in the local var (ppoid) so I can pass it as a parameter to the thankyou.php page, but it is the webhook that updates the database (again, I did not trust the javascript to be secure and unhackable - any call from this script to update the database would have been risky in my eyes)

 

And so the code for my webhook, just a single PHP file to handle all messages from the PayPal system:

 

<?php
// This file is called by PayPal and returns data relating to
// a payment transaction. It should never be seen by the customer.

// should result in 2 lines to log file for every inbound message (what was received followed by what was done with it)

// these includes are just functions to perform the database actions	
require('../includes/paypal_functions.inc');
require('../includes/paypal_orders.inc');

$date = date('Ymd');
$logfile= 'pp_results_'. $date . '.txt';

$t = file_get_contents('php://input');

// log every request to the log file
file_put_contents($logfile, (new DateTime())->format('Y-m-d H:i:s.u') . ' - Received ' . $t . chr(13) . chr(10), FILE_APPEND | LOCK_EX);

$oid = $ppoid = $pptid = '';
$details = json_decode($t);
if ($details == NULL) {
	// oops - failed to decode it - save that to file
	file_put_contents($logfile, (new DateTime())->format('Y-m-d H:i:s.u') . ' - Oops, failed to convert to JSON' . chr(13) . chr(10), FILE_APPEND | LOCK_EX);
	echo 'Failed to convert JSON';
} elseif (isset($details->resource->purchase_units[0]->reference_id)) { // from user authorised
	// use my oid
	$oid = $details->resource->purchase_units[0]->reference_id;
	if (isset($details->resource->purchase_units[0]->payments->catures[0]->id)) {
		$pptid = $details->resource->purchase_units[0]->payments->catures[0]->id;
	}
	// Locate record 
	echo 'Is just a notification of authorisation';
	$result = authorise($oid, $pptid); // my db method to handle an authorisation notification
	file_put_contents($logfile, (new DateTime())->format('Y-m-d H:i:s.u') . ' - Result of attempting to authorise $oid: $result' . chr(13) . chr(10), FILE_APPEND | LOCK_EX);
} elseif (isset($details->resource->id)) { //from capture complete - Success!
	// use PP TID
	$pptid = $details->resource->id;
	$tran_status = $details->resource->status;
	// could be a renewal or join, need to find the record to determine that
	echo 'Is a complete! ('.$tran_status.')';
	$result = complete($oid, $ppoid, $pptid, $tran_status); //my db method to handle a complete
	file_put_contents($logfile, (new DateTime())->format('Y-m-d H:i:s.u') . " - Result of attempting to complete $pptid: $result" . chr(13) . chr(10), FILE_APPEND | LOCK_EX);
}

?>

 

You will see that I only handle auth and capture notifications, everything else is logged but ignored.  You can see I have methods that update the database if I have an auth and if I have a capture, you don't need the detail of what I do there - you do what you need to do 🙂

Login to Me Too

FixItDik
Contributor
Contributor

PS I know my code could be better, it assumes that the existence of the JSON parameters means it is a capture for example but in experience I was only seeing those two web hooks anyway so I get away with it. You might want to add code that checks the call type.

Login to Me Too

FixItDik
Contributor
Contributor

PPS my thankyou page also does not update the database, it simply uses the {my order id} passed to it to locate my order record and checks the status.  On the sandbox the web hooks were happening hours after the transaction (due to issues at PayPal) but in Live they seam to be happening before the user gets redirected to the thankyou page so it is always seeing the status of the order as "Complete". If the thankyou page sees "Complete" it shows them the details of their completed order for them to print (my database methods used in the webhook code also sends them an email anyway so they have that for their records) *BUT* if it sees the order as "Pending" then is simply tells them they will get an email once PayPal confirm the transaction is complete. Sorry, possibly giving you too much information now but hope some of it is helping 🙂

Login to Me Too

whitedragon101
Contributor
Contributor

Ah I see perfect.  From your code I see the Webhook json payload gives you back the local database id for the transaction to match it.

Thats ideal.

 

$details->resource->purchase_units[0]->reference_id;

 

Will give this all a test now.

Again thanks so much for the help. Been stuck on this for ages.

Login to Me Too

AdamDev
Member
Member

In a nutshell...

 

  • Your PayPal dashboard does not show your reference_id even though it should and that needs fixing.
  • You pass the reference_id in and not a custom_id when you use createOrder() and place reference_id inside the purchase_units object at the same point as the amount object.

  • You can get the reference id returned via the JS Object orderData.purchase_units[0].reference_id

 

Login to Me Too

Haven't Found your Answer?

It happens. Hit the "Login to Ask the community" button to create a question for the PayPal community.