~/php/paypal-payment-issue-validating-payment-access-denied.md

Paypal Payment Issue While Validating Payment - Access Denied

2 min readJanuary 12, 2022by Gorav Singal#paypal#php
// TL;DR

The PayPal 'Access Denied' error on payment validation occurs when the validation endpoint URL changes; update the cgi-bin/webscr URL to the current PayPal endpoint.

Paypal Payment Issue While Validating Payment - Access Denied

Introduction

I was using Paypal payment on one of my website, and suddenly lot of complaints started coming that their payment is not reflected. The exact error that was coming is:

Access Denied.

You don't have permission to access "/cgi-bin/webscr" on this server.

Why Validation

Let me expand the scenario a bit more. When user initiate a payment, I redirect user to the Paypal website. When user do the payment, it calls my website URL with the payment details.

It is a best practice from security perspective to validate this payment from Paypal.

Old Code for Validation in Php

$req = 'cmd=_notify-validate';
if (function_exists('get_magic_quotes_gpc')) {
    $get_magic_quotes_exists = TRUE;
}
else {
    $get_magic_quotes_exists = FALSE;
}

foreach ($postVars as $key => $value) {
    if ($get_magic_quotes_exists == TRUE) {
        $value = urlencode(stripslashes($value));
    }
    else {
        $value = urlencode($value);
    }
    $req .= "&$key=$value";
}

$url = 'https://www.paypal.com/cgi-bin/webscr';

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));

if (!($res = curl_exec($ch))) {
  //invalid response
  curl_close($ch);
  return;
}
curl_close($ch);

if (strcmp($res, "VERIFIED") == 0) {
    $txn_type = strtoupper($_POST['txn_type']);

    switch ($txn_type) {
        // A subscriber has signed up for the service.
        case 'SUBSCR_SIGNUP':
            //handle_call1($res)
            break;

        case 'SUBSCR_CANCEL': // A subscriber cancelled a subscription.
        case 'SUBSCR_MODIFY': // A subscriber profile has been modified.
        case 'SUBSCR_FAILED': // A subscriber tried to pay for the service but things didn't work out.
        case 'SUBSCR_EOT': // A subscriber has reached the end of the subscription term.
            //do nothing
            break;

        // A subscriber has paid for the service.
        case 'SUBSCR_PAYMENT':
        case 'WEB_ACCEPT':
            //handle_call2($res);
            break;
    }
}
else {
    if (strcmp($res, "INVALID") == 0) {
        //Invalid response returned from Paypal
        return;
    }
}

Lets Focus on the main code:

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));

Working Earlier

This solution worked for years. Suddenly, without any information Paypal did change something at their side. And, it took lot of time for me to figure out. As, it was not expected from Paypal to implement the breaking changes.

And, the error was:

Access Denied.

You don't have permission to access "/cgi-bin/webscr" on this server.

The Solution

Paypal started accepting a User-Agent header, with your company name. I think, the company name must have minimum 5 characters.

The code now becomes:

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close', 'User-Agent: mycompany'));

And, it worked finally. Poor show Paypal. Hope it helps.

$ subscribe --weekly

One deep dive a week, in your inbox

System design, AI engineering, trading systems and interview prep. Tested code, real numbers. Free, and you can unsubscribe any time.

or read it on gyanbyte.substack.com

Share

Related Posts

How to install Mongo DB Driver for Php 7.x

How to install Mongo DB Driver for Php 7.x

The simplest way to install driver for php is using pecl. When I tried to run…

How to get all image tags from an html - php code

How to get all image tags from an html - php code

I wanted to fetch all image tags from a big html, and wanted to perform some…

Drupal 7 - Code for Exporting all your content nodes in json files

Drupal 7 - Code for Exporting all your content nodes in json files

Introduction When I migrated all of my drupal-7 website to drupal-8, I wrote…

Drupal: How to block a user by email programatically

Drupal: How to block a user by email programatically

Many times, while administering your drupal website, you must have encountered…

php55w-common conflicts with php-common-5.* | Php issues while installing libraries

php55w-common conflicts with php-common-5.* | Php issues while installing libraries

I was trying to install mongo extension with pecl. It gave me error: Then, I…

Drupal 7: How to save a node programmatically and add an image field from a public URL

Drupal 7: How to save a node programmatically and add an image field from a public URL

Note: I have public URLs of these images, which I want to save. return…

Latest Posts

How to Stay Valuable as a Software Engineer in the AI Era: A Practical Playbook

How to Stay Valuable as a Software Engineer in the AI Era: A Practical Playbook

In the previous article I argued that AI is not killing software engineering…

How to Review AI-Generated Code: A Checklist for Catching What the Model Missed

How to Review AI-Generated Code: A Checklist for Catching What the Model Missed

A few months back I approved a PR that was almost entirely AI-generated. It…

How to Debug AI-Generated Code: Understand First, Fix Last

How to Debug AI-Generated Code: Understand First, Fix Last

In the last article I wrote about reviewing AI code before it goes in. This one…

The Future of Software Engineering: What AI Changes, What Stays, and Where the Jobs Go

The Future of Software Engineering: What AI Changes, What Stays, and Where the Jobs Go

Every few weeks somebody sends me a scary headline — “AI will replace all…

The Transactional Outbox Pattern: Never Lose an Event Again

The Transactional Outbox Pattern: Never Lose an Event Again

This is one of those patterns that looks like nothing until you get burnt once…

Event Sourcing, Explained: Store What Happened, Not Just the Final State

Event Sourcing, Explained: Store What Happened, Not Just the Final State

Here is a question I like to ask in interviews: your bank account shows a…