4
votes

I'm developing a wordpress plugin that needed the PayPal Express checkout feature.

I was following this article from paypal site on how to Integrating the In-Context Checkout Feature (https://developer.paypal.com/webapps/developer/docs/classic/express-checkout/integration-guide/ECGettingStarted/#id53620a28-e357-4744-9979-66ed5c592183) which gave me the following form on 1 step.

<form method=post action=https://api-3t.sandbox.paypal.com/nvp>
    <input type=hidden name=USER value=API_username>
    <input type=hidden name=PWD value=API_password>
    <input type=hidden name=SIGNATURE value=API_signature>
    <input type=hidden name=VERSION value=XX.0>
    <input type=hidden name=PAYMENTREQUEST_0_PAYMENTACTION value=Sale>
    <input name=PAYMENTREQUEST_0_AMT value=19.95>
    <input type=hidden name=RETURNURL value=https://www.YourReturnURL.com>
    <input type=hidden name=CANCELURL value=https://www.YourCancelURL.com>
    <input type=submit name=METHOD value=SetExpressCheckout>
</form>

I have added my own API_username, API_password & API_Signature from PayPal but when submitted it shows ACK=Failure.

Following is the message from paypal I'm geting:

TIMESTAMP=2015%2d10%2d30T05%3a27%3a09Z&CORRELATIONID=24cb45b8dd36b&ACK=Failure&VERSION=0%2e000000&BUILD=18308778&L_ERRORCODE0=10002&L_SHORTMESSAGE0=Security%20error&L_LONGMESSAGE0=Security%20header%20is%20not%20valid&L_SEVERITYCODE0=Error

Because of this I cannot go to step 2 on the PayPal documentation, so need help here. What am I doing wrong?

3
Why did you remve the quotes? Those are important - Rob
I haven't removed the quotes. Its taken from the paypal site as it was there, though I have tested it with quotes still ACK = Failure - Omer
Yeah I just looked at the site, it has no quotes which is weird. You should put quotes. - ambe5960
Also there should be a way to see the paypal error message which will have a more specific error message - ambe5960
Did you also edit the version? I also wonder why this is such a werid setup - it looks like something paypal wrote in 1992. - Rob

3 Answers

10
votes

Try this code I used in my project and it works.

PHP:

$paypal_url='https://www.paypal.com/cgi-bin/webscr'; 
$paypal_id='[email protected]';

HTML:

<form action='<?php echo $paypal_url; ?>' method='post' name='frmPayPal1'>
    <input type='hidden' name='business' value='<?php echo $paypal_id;?>'>
    <input type='hidden' name='cmd' value='_xclick'>
    <input type='hidden' name='item_name' value='Products Total'>
    <input type='hidden' name='amount' value='<?php echo $tot;?>'>
    <input type='hidden' name='no_shipping' value='1'>
    <input type='hidden' name='currency_code' value='USD'>
    <input type='hidden' name='handling' value='0'>
    <input type='hidden' name='cancel_return' value='http://localhost/paypal/cancel.php'>
    <input type='hidden' name='return' value='http://localhost/paypal/success.php'>
    <input type="image" src="https://www.sandbox.paypal.com/en_US/i/btn/btn_buynowCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
    <img alt="" border="0" src="https://www.sandbox.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>
3
votes

To implement PayPal Express Checkout you need to do some server side code (PHP, ASP.Net, ...). What you are trying to do is an HTML form which won't work. You have to do 3 API calls to validate a full payment.

In each call you will use your API credentials.

3
votes

I recommended to implement PayPal Express Checkout API.

There is a lot benefits to use this method instead that you use for creating a wordpress plugin. The main reason is that the response will be delivered immediately and you will not use a IPN methods to check if transaction is successful. Also you can add several recurring and one time payments in one session, including taxes and benefits. You will have the freedom to add all this stuff, practically everything - that's need for your wordpress plugin to be cool!

This is just as recommendation. Here one simple (PHP) example how to set a transaction with several products (also recurring payment):

// Parameters for SetExpressCheckout, which will be sent to PayPal
$padata['L_BILLINGAGREEMENTDESCRIPTION0'] = 'Product description'; $padata['L_BILLINGAGREEMENTDESCRIPTION0'] = $padata['L_BILLINGAGREEMENTDESCRIPTION0'] . ' $'.$product->price.'/month'; $padata['L_PAYMENTREQUEST_0_DESC0'] = $padata['L_BILLINGAGREEMENTDESCRIPTION0'] . ' $'.$product->price.'/month';
$padata['PAYMENTREQUEST_0_NOTIFYURL'] = 'http://site_url/paypal/ipn'; $padata['PAYMENTREQUEST_0_DESC'] = $product->name; $padata['RETURNURL'] = 'http://site_url/paypal/returnurl'; $padata['CANCELURL'] = 'http://site_url/paypal/cancelurl';
$padata['PAYMENTREQUEST_0_CURRENCYCODE'] = 'USD'; $padata['PAYMENTREQUEST_0_PAYMENTACTION'] = 'SALE'; $padata['PAYMENTREQUEST_0_ITEMAMT'] = $product->price;
$padata['PAYMENTREQUEST_0_AMT'] = $product->price;
$padata['L_BILLINGTYPE0'] = 'RecurringPayments';
$padata['L_PAYMENTREQUEST_0_NAME0'] = $product->name;
$padata['L_PAYMENTREQUEST_0_NUMBER0'] = '322';
$padata['L_PAYMENTREQUEST_0_QTY0'] = '1';
$padata['L_PAYMENTREQUEST_0_AMT0'] = $product->price; $padata['L_PAYMENTREQUEST_0_NAME1'] = 'Second Product name';
$hosteddata['L_PAYMENTREQUEST_0_DESC1'] = 'second product description';
$hosteddata['L_PAYMENTREQUEST_0_NUMBER1'] = $secondproduct->id;
$hosteddata['L_PAYMENTREQUEST_0_QTY1'] = '1';
$hosteddata['L_PAYMENTREQUEST_0_AMT1'] = $secondproduct->price; $paypal_data = http_build_query($padata); $httpParsedResponseAr = $this->PPHttpPost('SetExpressCheckout', $paypal_data); //Respond according to message we receive from Paypal if("SUCCESS" == strtoupper($httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($httpParsedResponseAr["ACK"])){ //Redirect user to PayPal store with Token received. $paypalurl ='https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token='.$httpParsedResponseAr["TOKEN"].''; header('Location: '.$paypalurl); }else{ echo 'Error : '.urldecode($httpParsedResponseAr["L_LONGMESSAGE0"]).'';
}

Page returnurl:

$hosteddata['L_BILLINGAGREEMENTDESCRIPTION0']   = 'Recurring Description';
$hosteddata['L_BILLINGAGREEMENTDESCRIPTION0'] = $hosteddata['L_BILLINGAGREEMENTDESCRIPTION0'] . ' $'.$pr->price.'/month';
$hosteddata['L_PAYMENTREQUEST_0_NAME0'] = $pr->name;
$hosteddata['PROFILEREFERENCE'] = $GetExpressCheckoutDetails['L_PAYMENTREQUEST_0_NUMBER0'];
$hosteddata['PROFILESTARTDATE'] = date('Y-m-d') . 'T' . date('H:i:s').'Z';
$hosteddata['SUBSCRIBERNAME'] = $GetExpressCheckoutDetails['FIRSTNAME'] . ' ' . $GetExpressCheckoutDetails['LASTNAME'];
$hosteddata['TOKEN'] = urlencode($_POST['token']);
$hosteddata['DESC'] = $hosteddata['L_BILLINGAGREEMENTDESCRIPTION0'];
$hosteddata['AMT'] = $pr->price;
$hosteddata['BILLINGPERIOD'] = 'Month';
$hosteddata['BILLINGFREQUENCY'] = '1';
$hosteddata['TOTALBILLINGCYCLES'] = '12';
$hosteddata['REGULARTOTALBILLINGCYCLES'] = '1';
$hosteddata['VERSION'] = '74.0';
$hosteddata['MAXFAILEDPAYMENTS'] = '1';
$hosteddata['L_PAYMENTREQUEST_0_QTY0'] = '1';
$hosteddata['L_BILLINGTYPE0'] = 'RecurringPayments';
$hosteddata['L_PAYMENTREQUEST_0_ITEMCATEGORY0'] = 'Digital';
$hosteddata['L_PAYMENTREQUEST_0_AMT0'] = $pr->price;
$hosteddata['INITAMT'] = $pr->price;
$hosteddata['L_PAYMENTREQUEST_0_NUMBER0'] = $pr->id;
$hosteddata['PAYMENTREQUEST_0_NOTIFYURL'] = 'http://site_url/paypal/ipn';
$padata['L_PAYMENTREQUEST_0_NAME1'] = 'Second Product name'; $hosteddata['L_PAYMENTREQUEST_0_DESC1'] = 'second product description';
$hosteddata['L_PAYMENTREQUEST_0_NUMBER1'] = $secondproduct->id;
$hosteddata['L_PAYMENTREQUEST_0_QTY1'] = '1';
$hosteddata['L_PAYMENTREQUEST_0_AMT1'] = $secondproduct->price; $paypal_data = http_build_query($hosteddata); $hosted_saas_response = $this->PPHttpPost('CreateRecurringPaymentsProfile', $paypal_data);

I used a separate method to post parameters to paypal

private function PPHttpPost( $methodName_, $nvpStr_ ) {
$api_username = '[email protected]'; $api_password = 'QWEQWEWQEQWEQEQWE';
$api_signature = 'WQEQWEQWEQWEWQEQWEQWEQWEQWEQWE.cT';
$api_endpoint = "https://api-3t.paypal.com/nvp";
$version = '124.0'; $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_endpoint);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
$nvpreq = "METHOD=$methodName_&VERSION=$version&PWD=$api_password&USER=$api_username&SIGNATURE=$api_signature&$nvpStr_";
curl_setopt($ch, CURLOPT_POSTFIELDS, $nvpreq);
$httpResponse = curl_exec($ch);

if(!$httpResponse) { exit("$methodName_ failed: ".curl_error($ch).'('.curl_errno($ch).')');
}

// Extract the response details.
$httpResponseAr = explode("&", $httpResponse);
$httpParsedResponseAr = array();

foreach ($httpResponseAr as $i => $value) {
$tmpAr = explode("=", $value);
if(sizeof($tmpAr) > 1) {
$httpParsedResponseAr[$tmpAr[0]] = $tmpAr[1];
}

} if((0 == sizeof($httpParsedResponseAr)) || !array_key_exists('ACK', $httpParsedResponseAr)) {
exit("Invalid HTTP Response for POST request($nvpreq) to $api_endpoint.");
} return $httpParsedResponseAr;
}