Angular 6 Redirect to external url using POST
Asked Answered
W

3

9

I am trying to incorporate payment gateway into angular app, I found following JavaScript snippet provided by the payment partner for incorporation, I tweaked it by adding ngNoForm and got it working with angular; stackBlitz

<form ngNoForm method="post" action="https://test.pesopay.com/b2cDemo/eng/payment/payForm.jsp">
<input type="hidden" name="amount" value="1" >
<input type="hidden" name="currCode" value="608" >
<input type="hidden" name="payMethod" value="CC">
<input type="submit" name="submit">
</form>

Next instead of using HTML <Form> POST + Action directly from component template, I wanted to make a REST call to my back end server perform some other steps and map that API response into what my payment partner expects and and then redirect to my payment partner using HttpClient POST which would supposedly do the same thing and redirect to the payment partner website which is supposed to take care of the payment process, so in short I wanted to achieve the same thing programmatically, I tried:

redirectToPaymentHandler(): Observable<any> {
  const urlEncodeData = 'amount=1&currCode=608&payMethod=CC';
  let headers = new HttpHeaders();
  headers = headers.append('Content-Type', 'application/x-www-form-urlencoded');
  headers = headers.append('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8');

  return this.httpClient.post('https://test.pesopay.com/b2cDemo/eng/payment/payForm.jsp',
    urlEncodeData, {
      headers,
      responseType: 'text'
    }).pipe(catchError((error) => {
      console.log(error)
      return EMPTY;
    }));
}

Initially I encountered CORS issue, but I was able to bypass that using Angular CLI's proxy config, now what happens is that instead of redirecting to the payment partner's website, HttpClient's POST method actually returns the web page as a string from the service endpoint, which is not what I though it would do,

Kindly refer to this stackBlitz, for further understanding.

So to sum it all up, basically I am seeking to transform above working template driven POST request into programmatic version. so I could perform some set of operations first and then redirect to the external URL using POST. I believe what I am trying programmatically is probably different from what happens against Html <form> POST + Action. There must be some programmatic equivalent of the same thing.

P.S: I did found this similar question but sadly it has no working answer.

P.S.S: I can not use window.location.href or RouterLink as its supposed to be POST and there is supposed to be info like amount, currency and other things to be passed to the payment partner along side.

Wille answered 25/11, 2018 at 21:20 Comment(9)
Use your initial form, but hide it using css, and use JavaScript to programmatically submit it.Arvie
you are asking essentially the same question again - and ignoring the most standard answer: have your back end server that would take your Angular request, submit its own request to payment processor, and return JSON back to Angular client. proxy config may help you in development; but you will have the same problem once you move your code to productionElectret
@Electret have you opened provided stackBlitz? I bet you haven't. The thing is pesoPay provides two options for payment handling, API and redirecting to pesoPay for payment handling. I am not trying to integrate any API here, what I am trying to do is simply a POST redirection. Which is the reason my back end is not required to do the talking with my payment partner. I didn't highlighted that point well enough in my previous question which is the reason I deleted that. Is it possible to do a POST redirect via Angular? similar to what form post + action does?Wille
@JBNizet I know that would be possible, but I don't think that would be the recommended way, for simplicity purpose I have only added three properties i.e. amount, currCode and payMethod, but in actual there can be 15+ properties which has to be handled. I personally don't like the idea of performing tasks via creating unnecessary DOM nodes, I bet this would be do able via code that's why no need to complicate things,Wille
I did... but you don't listen to what people told you on that thread. Browsers POST forms and receive HTML back; Javascript POST xhr and receive JSON. You are hitting the wall that is there by design (maybe bad design; but 20 years ago nobody predicted the patterns that are in use today, like SPA). If you insist that your back end is not required - then you will continue to hit the same wall. Good luck - if you don't get results, you will get experience :)Electret
@Electret I know how JS uses POST, but I didn't know that browser uses POST differently, may be because I live in the world of SPAs, where you don't need to use browser POST normally. p.s, I wasn't told any where what I am trying is not possible, what I was only told was how to avoid CORS, I know I can use Back end, to avoid CORS but my question mainly focuses on why browser POST works, and JS POST doesn't. you could have simply corrected me at the root by pointing this out earlier, anyway thanks. I still think there could be a way around, If there isn't then I surely learnt some thing new.Wille
@Wille Hi Saif did you get any solution on this. I am facing same problemLouvenialouver
@Louvenialouver - if your problem is truly the same, then the solution is quite simple; whether OP decided to implement it or not... If you still have a problem, maybe it's worth posting it as a questionElectret
I think you should contact PesoPay to inform them that they're opening themselves up for CSRF attacks. I can embed a button on my own website, and if you click it you make a transaction of 5000 dollars to my bank account, while being signed in with the cookie of PesoPay...Chrissa
S
2

Given the scenario we can achieve the desired output by submitting a dynamically created form by using plain JavaScript instead of HttpClient. When we use HttpClient, redirection occurs in the network and not in the client browser. Here is my solution

const myform = document.createElement('form');
myform.method = 'POST';
myform.action = "https://test.pesopay.com/b2cDemo/eng/payment/payForm.jsp";
myform.style.display = 'none';
myform.append('Content-Type', 'application/x-www-form-urlencoded');
myform.append('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8');
document.body.appendChild(myform);
myform.submit();
Stink answered 20/8, 2020 at 4:55 Comment(2)
hie david / priyanka, the same solution working fine in all browsers, expect in iphones (safari.) ... I'm getting network connection lost error while performing form posting.. could you please advise or provide your inputs on this.Rang
Hi David / Privanka, This works fine in Chrome but headers are not working. Any idea?Optometer
B
1

Initially I didn't think this would work, but after spending the whole week researching and trial and error. It is working nicely for me using Angular 6. You probably can use javascript or any other language you want. Basically, 2 steps you must follow:

1) When calling the login form redirect, you must set respontType=text so HttpClient won't throw an error expecting JSON data.

return  this.http.post(this.HOST_URL+'login' ,params1.toString(), 
{
    headers:  headers,
    withCredentials: true,
    observe: 'response',
    params: params1,
    responseType: 'text',
    reportProgress: true
})

2) Setup an interceptor to intercept your subsequence API request (assuming the endpoint will return json now). In the intercepter, pass withCredentials=true . In Javascript or Curl, you would expect to retrieve that from the login successful and send back JSESSIONID with the API request. In Angular, you only need withCredentials=true in the Interceptor. *NOTE: you cannot pass this in the HttpHeaders before making a request. See bug report here.

Here is my code snippet for Interceptor:

@Injectable()
export class WithCredentialsInterceptor implements HttpInterceptor {

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        console.log("Intercepting Request withCredentials=true ");
        request = request.clone({
            withCredentials: true
        });

        return next.handle(request);
    }
}

Hope this will help.

Burress answered 7/12, 2018 at 20:58 Comment(2)
Can you make your code examples more complete? For example, what is params1? What is headers? What are your imports?Kristoforo
@Jak, It is not about API calling, it is redirect to external URL (not API). So this will not work.Optometer
T
1
const mapForm = document.createElement('form');
mapForm.method = 'POST';
mapForm.action = `${b2pApiUrl}/Purchase`;
mapForm.style.display = 'none';

const mapInput = document.createElement('input');
mapInput.type = 'hidden';
mapInput.name = 'param1';
mapInput.value = param1;
mapForm.appendChild(mapInput);

const mapInput1 = document.createElement('input');
mapInput1.type = 'hidden';
mapInput1.name = 'param2';
mapInput1.value = param2;
mapForm.appendChild(mapInput1);
....
document.body.appendChild(mapForm);

mapForm.submit();
Tricuspid answered 27/7, 2020 at 13:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.