0

I'm implementing a credit card payment component in an Angular 4 application which uses the Paypal Payflow API to process credit card payments. I've already created testing and production accounts in Paypal Manager (those credentials have to be send in the request to the Payflow API).

According to the Payflow documentation, this is a very basic example that shows how to post a request to the Payflow API:

curl https://pilot-payflowpro.paypal.com \
  -s \
  --insecure \
  -d PARTNER=PayPal \
  -d PWD=MyPassword \
  -d VENDOR=MyMerchantID \
  -d USER=MyMerchantID \
  -d TENDER=C \
  -d ACCT=5105105105105100 \
  -d TRXTYPE=S \
  -d EXPDATE=1221 \
  -d AMT=1.00

Since I'm developing an Angular application, I'm using the HttpClient library to make post requests to the Payflow API.

In order to post test transactions I use Postman. On Postman, I execute a POST request to https://pilot-payflowpro.paypal.com and send something like the string below in the request body:

USER=MyUser&PARTNER=PayPal&VENDOR=MyVendor&PWD=MyPassword&ACCT=4012888888881881&EXPDATE=102020&TENDER=C&TRXTYPE=A&AMT=100&VERBOSITY=HIGH

Once I execute that request in Postman, It works just fine and the server returns the expected string response, which is a string like the one below:

RESULT=0&SECURETOKEN=7mHAFU7JlLEGhzUHy7VD4rQL2&SECURETOKENID=123456789123456789123456789123456781&RESPMSG=Approved

Now, I implemented an Angular 4 application, which uses the HttpClient Angular Library to execute post transactions; the request looks like the code below:

private requestPayflowPaymentToPayflowGatewayApi( requestString: string ): Observable<string> {
      const headers: Headers = 
      new Headers( { 'Content-Type': 'text/plain'});
      const serverUrl = 'https://pilot-payflowpro.paypal.com';
      const body = 'USER=gtcdevelopment&PARTNER=PayPal&VENDOR=gtcdevelopment&PWD=k4l1m4nt4n&ACCT=5105105105105100&EXPDATE=102020&TENDER=C&TRXTYPE=A&AMT=100&VERBOSITY=HIGH&';

      return this.oldHttp.post(serverUrl, body, {headers: headers, responseType: ResponseContentType.Text }).catch( ( error ) => {
      console.log('error in requestPayflowPaymentToPayflowGatewayApi');
      console.error(error);
      return Observable.throw(error);
    } );
  }

It's just a basic post request, but it doesn't work when I execute the application in the browser; I get the error below in the console log:

The error is caught in the .catch() portion of the observable code (post() method).

{  
   "_body":{  
      "isTrusted":true
   },
   "status":0,
   "ok":false,
   "statusText":"",
   "headers":{  

   },
   "type":3,
   "url":null
}

Here are some captures of my console log:

enter image description here enter image description here

Now, there is something really strange about it; When I disable web security in Google Chrome, which I do by opening Google Chrome running the command below in Windows, the application works just fine, executes the request to the server and it returns the expected response.

chrome.exe --user-data-dir="C:/Chrome dev session" --disable-web-security

I have the same issue when I post request using production data (a production Paypal Manager Account pointing to https://payflowpro.paypal.com)

Have anybody had this issue? What do you think that could be the problem here?

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Thisisalexis
  • 104
  • 5

1 Answers1

1

It looks like you are running into a CORS (Cross-Origin Resource Sharing) error. Basically, your web browser (all web browsers) won't send a request to a sever (an "Origin") that is not the server that your page / app originally came from, unless the destination server says it's ok to do so.

For details of the mechanics of this, see my previous answer to a related but slightly different question: Response for preflight does not have HTTP ok status in angular

I'm not familiar with PayPal's API, but to make this work, PayPal's server would have to respond to your request (actually, the browser's OPTION request) with a header that tells your browser that it's ok to actually send the real request.

When you use the --disable-web-security option with Chrome, you are turning off this CORS process, so the API works - a really bad idea.

When you use curl, you are similarly not involving the browser's CORS protection, and the API works.

If there is no way to make the API return the correct header so the request works from your browser-based Angular client, then the only option that I'm aware of is to set up your own server-side process (at the same domain / port that your Angular app is served from) that proxies the request from the browser.

GreyBeardedGeek
  • 29,460
  • 2
  • 47
  • 67
  • Hello and thanks for your reply! I'm pretty sure you're right, since when I post the request without any header, I also see the below message in the console log: Failed to load https://pilot-payflowpro.paypal.com/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access. – Thisisalexis Aug 30 '18 at 16:09