Paypal Sandbox IPN "Email does not match seller" - php

I am trying to create a paypal subscription button with paypal sandbox. I have created the dev account, created sandbox business and test accounts. Logged into my business test account, went to merchant services -> my saved buttons.
Found the subscription button and copied the code.
I have uploaded the form to my codeigniter controller at: http://422clients.com/ftg/paypal/test
I am using this paypal IPN library for codeigniter: https://github.com/orderly/codeigniter-paypal-ipn
I am using unmodified controller code for now to handle the IPN response because it writes it to the database just fine for testing:
function ipn()
{
$this->load->library('PayPal_IPN'); // Load the library
// Try to get the IPN data.
if ($this->paypal_ipn->validateIPN())
{
// Succeeded, now let's extract the order
$this->paypal_ipn->extractOrder();
// And we save the order now (persist and extract are separate because you might only want to persist the order in certain circumstances).
$this->paypal_ipn->saveOrder();
// Now let's check what the payment status is and act accordingly
if ($this->paypal_ipn->orderStatus == PayPal_IPN::PAID)
{
//Enable database subscription...
}
}
else // Just redirect to the root URL
{
$this->load->helper('url');
redirect('/', 'refresh');
}
}
My problem is that when i click the button on the site and pay with my sandbox test account everything looks totally fine and the payment goes through, but when I look in my database it says: Status: ERROR.
Reason Field says: email business_test#422studios.com does not match seller
business_test#422studios.com is the sandbox account that i generated the button from.
I'm totally lost here. Any help is appreciated.

In your config file, make sure you have this:
$config['paypal_ipn_sandbox_settings'] = array(
'email' => 'business_test#422studios.com',
'url' => 'https://www.sandbox.paypal.com/cgi-bin/webscr',
'debug' => TRUE
);
And that you pass this value in your form:
<input type="hidden" name="business" value="business_test#422studios.com">

Related

PHP verify Paypal webhook signature

I'm having trouble trying to verify paypal webhook signatures with PHP. Using the new V2 of paypals APIs I am receiving the paypal webhook on my page.
However I can not seem to successfully validate the signature.
From the link HERE I got some sample webhook validation PHP code from paypal.
I can not get it working, I don't know where I am supposed to get bootstrap.php from in the paypal code. The paypal information seems incomplete or half baked. Paypal seems to be terrible to set-up compared to Stripe.
Has anyone got experience of validating paypal webhook signatures with PHP when using V2 of the paypal APIs ?
Well I have come to the conclusion that the Paypal developer information is rather poor, it is scatted all over the place, on multiple different pages and sites. The examples they give on the paypal developer website HERE are not a complete picture of what is required to validate a webhook signature. Stripe developer documentation is much better formatted and concise.
Installing Paypal Checkout V2 SDK does not give you the necessary development tools to validate paypal webhook signatures i.e. you can process payments and receive webhooks but you can not validate the webhook signatures....I know stupid. Tip do not download the SDK directly as you will not include the required autoload.php file. Use composer to install Paypal Checkout V2 SDK so that you get the autoload.php file.
Once you are to a point that you can process payments and receive webhooks form paypal you need to install the another SKD called Paypal Rest API SDK. Again use composer to install the SDK so that you get a autoload.php file which you will need.
When you install Paypal Rest API SDK amazingly you will still be missing files that are required to validate the payapl webhook signatures. I can find no mention of these anywhere on the paypal developer website.
bootstrap.php & common.php
Thanks to #Grumpy I got some samples provided on github HERE
Note you will probably need to modify the samples a bit in order to get them working with your website. Tip set the logger to false and save yourself some bother if you don't have the necessary access permissions to write.
Once you have bootstrap.php & common.php files created you can write the code for your webhook endpoint page i.e. the page that paypal sends the webhook to. I have included my PHP code below for how to validate and then process the paypal webhook. Tip in the below code you need to specify the webhook ID, each webhook that you create in paypal has a unique ID. Also when you are testing you can not use webhook simulator as this will fail validation, you can make manually a payment with your sandbox account details which will trigger a webhook payment event.
Paypal sure don't make it easy, their documentation is all over the place compared to Stripe. The Paypal webhooks can sometimes take several minutes to arrive after a payment is made, very frustrating when trying to debug. Also it's a bit ridiculous that they have a webhook simulator on the paypal developer website that can not be used to validate signatures...if stripe can do it why can't paypal.
<?php
//get the webhook payload
$requestBody = file_get_contents('php://input');
//check if webhook payload has data
if($requestBody) {
//request body is set
} else {
//request body is not set
exit();
}
use \PayPal\Api\VerifyWebhookSignature;
use \PayPal\Api\WebhookEvent;
$apiContext = require __DIR__ . '/bootstrap.php';
//Receive HTTP headers that you received from PayPal webhook.
$headers = getallheaders();
//need header keys to be UPPERCASE
$headers = array_change_key_case($headers, CASE_UPPER);
/*
example header paypal signature content for webhook, these values are recieved as an array, we then need to use this data to verify the payload
CONTENT-LENGTH : 1376
CORRELATION-ID : 6db85170269e7
USER-AGENT : PayPal/AUHD-214.0-54377828
CONTENT-TYPE: application/json
PAYPAL-AUTH-ALGO : SHA256withRSA
PAYPAL-CERT-URL : https://api.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a784-5edc0ebc
PAYPAL-AUTH-VERSION : v2
PAYPAL-TRANSMISSION-SIG : Hc2lsDedYdSjOM4/t3T/ioAVQqFPNVB/AY/EyPNlavXk5WYUfnAmt9dyEP6neAPOjFHiVkXMK+JlLODbr6dalw6i26aFQdsPXqGl38Mafuu9elPE74qgsqNferUFgHi9QFXL+UZCNYcb4mvlDePXZIIAPbB0gOuFGOdEv2uqNwTCSAa/D8aguv1/51FWb3RkytFuVwXK/XNfIEy2oJCpDs8dgtYAZeojH8qO6IAwchdSpttMods5YfNBzT7oCoxO80hncVorBtjj1zQrkoynEB9WNNN9ytepNCkT8l29fQ4Sx/WRndm/PESCqxqmRoYJoiSosxYU3bZP7QTtILDykQ==
PAYPAL-TRANSMISSION-TIME : 2020-04-05T14:40:43Z
PAYPAL-TRANSMISSION-ID : 6dec99b0-774b-11ea-b306-c3ed128f0c4b
*/
//if any of the relevant paypal signature headers are not set exit()
if(
(!array_key_exists('PAYPAL-AUTH-ALGO', $headers)) ||
(!array_key_exists('PAYPAL-TRANSMISSION-ID', $headers)) ||
(!array_key_exists('PAYPAL-CERT-URL', $headers)) ||
(!array_key_exists('PAYPAL-TRANSMISSION-SIG', $headers)) ||
(!array_key_exists('PAYPAL-TRANSMISSION-TIME', $headers))
)
{
exit();
}
//specify the ID for the webhook that you have set up on the paypal developer website, each web hook that you create has a unique ID
$webhookID = "ENTER_YOUR_WEBHOOK_ID_HERE";
//start paypal webhook signature validation
$signatureVerification = new VerifyWebhookSignature();
$signatureVerification->setAuthAlgo($headers['PAYPAL-AUTH-ALGO']);
$signatureVerification->setTransmissionId($headers['PAYPAL-TRANSMISSION-ID']);
$signatureVerification->setCertUrl($headers['PAYPAL-CERT-URL']);
$signatureVerification->setWebhookId($webhookID);
$signatureVerification->setTransmissionSig($headers['PAYPAL-TRANSMISSION-SIG']);
$signatureVerification->setTransmissionTime($headers['PAYPAL-TRANSMISSION-TIME']);
$signatureVerification->setRequestBody($requestBody);
$request = clone $signatureVerification;
try {
$output = $signatureVerification->post($apiContext);
} catch (Exception $ex) {
//error during signature validation, capture error and exit
ResultPrinter::printError("Validate Received Webhook Event", "WebhookEvent", null, $request->toJSON(), $ex);
exit(1);
}
$sigVerificationResult = $output->getVerificationStatus();
// $sigVerificationResult is a string and will either be "SUCCESS" or "FAILURE"
//if not webhook signature failed validation exit
if($sigVerificationResult != "SUCCESS"){
exit();
}
else if($sigVerificationResult == "SUCCESS"){
//paypay webhook signature is valid
//proceed to process webhook payload
//decode raw request body
$requestBodyDecode = json_decode($requestBody);
//pull whatever info required from decoded request body, some examples below
$paymentSystemID = $requestBodyDecode->id;
$eventType = $requestBodyDecode->event_type;
//do something with info captured from the webhook payload
}
This seems to work with php 8.1:
if (openssl_verify(
data: implode(separator: '|', array: [
$httpPayPalTransmissionId,
$httpPayPalTransmissionTime,
$webhookID,
crc32(string: $rawRequestBody),
]),
signature: base64_decode(string: $httpPayPalTransmissionSignature),
public_key: openssl_pkey_get_public(public_key: file_get_contents(filename: $cachedHttpPayPalCertUrl)),
algorithm: 'sha256WithRSAEncryption'
) === 1) {
die('OK');
} else {
die('FAILED');
}

Contact Form 7 and PayPal IPN integration

I need add PayPal IPN on my site in a specific contact form.
I have integrate the funcions for IPN (listener and other) but I don't known how to invoke the Paypal from the CF7" and send the email only AFTER the payment confirm.
I think I have to use the wpcf7_before_send_mail function, but I don't know how.
I add this simple wpcf7_before_send_mail function in my functions.php
add_action("wpcf7_before_send_mail", "wpcf7_do_something_else");
function wpcf7_do_something_else($cf7) {
// get the contact form object
$wpcf = WPCF7_ContactForm::get_current();
$form_id = $contact_form->posted_data['_wpcf7'];
if ($form_id == 2969) {
if (/*Check if payment in the PayPal it's ok */) {
//Send mail and thank you page
} else {
$wpcf->skip_mail = true;
}
}
return $wpcf;
}
But I don't known to check the IPN (i think first I have redirect to paypal url and after i need listen the paypal response).
Also, i have insert in fuction only the $wpcf->skip_mail = true; but the mail but the email is sent however.
Can you help me?
Regards,
Marco
If this is a form post that then redirects the user to a PayPal Payments Standard checkout, there will be no completed payment until later (and some portion of payments will go unpaid), so you will not be able to check for payment at the time the form is posted. That comes later.
The variable notify_url to be specified for IPN is documented here.

How to return from PayPal purchase and display a success or failure message?

I can't seem to figure out proper way to handle the return url from a paypal purchase and display an alert message.
Here is how I understand process:
From my site send user to paypal, having set returnurl to be the url corresponding to what they purchased.
Paypal processes, then hits my IPN listener
User sees message in paypal window and button Return to merchant (I am in sandbox testing)
User hits Return to Merchant and is back to the page where they made purchase from
Ok, so in my IPN listener I do this:
if( ! session_id() ) {
session_start();
}
then a bunch of checks, then
$_SESSION['alert-success'] = 'Thank you for your purchase!';
or
$_SESSION['alert-danger'] = 'There was a problem processing your order';
Now in this IPN listener I can set more fine tuned messages, but for now trying to get something basic working.
Then in the product page they are returned to I have this
<div class="flash-message">
<?php
foreach (['danger', 'warning', 'success', 'info'] as $msg) {
$msgtype = 'alert-' . $msg;
$issession=0;
if(isset($_SESSION[$msgtype])) {
$p = "<p class=\"alert " . $msgtype. "\">" . $_SESSION[$msgtype] . "</p>";
echo $p;
$issession=1;
}
}
if ($issession) {
session_destroy();
}
?>
</div>
Now, this isn't doing anything. So it seems whatever session variable is being set in IPN listener is not available when user gets back to the return url.
I have in my paypal form this
<input type="hidden" name="rm" value="2">
in order to have the $_POST data and I confirmed that when user hits Return to Merchant the post data is there with 'payer_status' => string 'VERIFIED'
So what gives? What is the point of having an IPN listener do a bunch of work, if the return url is only coming back with minimal info? It is the status info I get in IPN listener that I want to be able to display in my flash message area, and I thought my using session variables would achieve that but was wrong.
All above said, what is the proper way to do this?
Thanks!
The reason using sessions doesn't work because paypal is the one sending data to the IPN listener. The session wouldn't be stored on the user's browser since they aren't the one sending data to the IPN listener. Can you not store the information gathered from the IPN listener into a database? That's what I did for my paypal purchases. When the payment successfully went through, I stored the payment data gathered from the IPN listener and stored it with their user ID in the database. When they were redirected to a success page, I grabbed the information from the database and displayed it.

how to handle paypal notify url in our site?

I have paypal form which will have ipn notify url. i have setup notify url in my paypal ipn settings. but it doesn't return to my site. after analyzed for more than 2 days. i found that my controller construct itself i check for current user session exist or not? should i remove the session check in construct not to check for session. Please advise on this.
this is my code. i am using codeigniter application.
function __construct() {
parent::__construct();
$this->load->model('user_model');
// if (!isEmployee()) {
// redirect('information');
//
// }
}
function paypal_ipn(){
$txn_id = $_POST['txn_id'];
$data = array("paypal_transaction_id"=>$txn_id);
$this->db->insert("payments",$data);
}
I have commented out now
Notify url is part of the Instant Payment Notification (IPN) mechanism of PayPal. This URL is called by PayPal to notify about a payment. It is not part of the user session at all so do not check for it.
I recommend you to read the documentation and get the example from here:
documentation IPN

pay pal returnurl to return users pay pal account email address

I'm using PHP.
Is it possible when paypal returns to my site after taken the payment that it could also pass me the email address (paypal username) so that I may use this to send them an email?
I'm trying to keep my site as simple as possible and I don't want to ask the customer for a load of information up front.
My ideal flow would be:
Take Payment > Add record to database > email customer with username and password
The customer can then log in and fill the rest in at their own leisure.
Is it possible to get this variable?
Thanks in advance.
Yes...see the receiver_email variable that was passed back.
Guide here: https://cms.paypal.com/cms_content/GB/en_GB/files/developer/IPNGuide.pdf
See Pages 42-43
This page shows the things you can get back from PayPal after they process a payment.
IPN and PDT Variables
Here is on example I made in Codeigniter.
/* Receives a form data from paypal that is processed after payment goes through.
* It will update the database entry with a transaction id.
*/
function epayment_notify(){
$this->load->model('pament_notice_model');
echo "Hi<br>";
$postvars = isset($_POST)? $_POST : array("no post");
/* echo "</pre>Post Vars:<br><pre>";
print_r($postvars);
echo "</pre>Refurl:<br><pre>";
Check for the transaction ID and put it in the pament_notice table if possible.
*/
if (isset($postvars['item_number']) && $postvars['item_number'] > 0 && isset($postvars['txn_id'])){
/* make sure that payment_number is empty on this row. */
log_message('info',"PayPal transaction received.");
if ($this->pament_notice_model->make_sure_payment_number_is_empty($postvars['item_number'])){
//writes the message to the local log file in CI
log_message('debug',"PayPal payment number is empty.");
/* set the txn_id in the slot */
$data = array('payment_number' => $postvars['txn_id']);
$this->db->where('entry_id',$postvars['item_number']);
$this->db->update('pament_notice',$data);
log_message('debug',"payment number updated. item:".$postvars['item_number']);
log_message('debug',"payment actual cost. payment_gross:".$postvars['payment_gross']);
} else {
log_message('error',"PayPal transaction attempted on non-empty payment number.");
if (isset($postvars['item_number']))
log_message('error',"failed request item_number :".$postvars['item_number']);
if (isset($postvars['txn_id']))
log_message('error',"failed request txn_id:". $postvars['txn_id']);
}
} else {
log_message('info',"PayPal payment transaction sent with invalid data.");
if (!isset($postvars['item_number']))
log_message('error',"item_number is not set");
if (isset($postvars['item_number']))
log_message('error',"failed request item_number :".$postvars['item_number']);
if (!isset($postvars['txn_id']))
log_message('error',"txn_id is not set\n");
if (isset($postvars['txn_id']))
log_message('error',"failed request txn_id:". $postvars['txn_id']);
}
}
There are a couple of way of handling this.
The easiest method is to collect that data prior to sending the user to paypal.
Store it in the users current session, and have paypal redirect the user back to your page automatically.
The second is to set up the Payment Data Transfer option. I haven't fully implemented this but there is Documentation on paypal's developer site. It basically sends a token back to your site when the user makes payment and you have to perform another HTTP post to retrieve the user data.

Categories