1
votes

I get status code 201 but when I try redirect to the approval_url I get

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

I try to not redirect and all work just fine, when I click the approval_url manually.

This is my code:

var express = require("express");
var router  = express.Router();
var middleAware = require("../middleware");
var paypal = require("paypal-rest-sdk");
var User    = require("../models/user");
var Shoe    = require("../models/shoe");

paypal.configure({
    'mode': 'sandbox', //sandbox or live
    'client_id': 'AUN6mxOAFtJS5hrlUGdyd-Fe1VOE6zu63W6dYztXhOXOpeT0ps9JbF9N3lII-f3EP1o7G2MHs9flc3Ho',
    'client_secret': 'someSecretId'
  });

//pay with paypal
router.post("/cart/:id/pay", function(req, res){
    User.findById(req.params.id).populate("cart").exec(function(err, foundUser){
        if(err){
            console.log(err);
            res.redirect("back")
        }else{
            //find all shoes to checkout
            var newItems = [];
            for(var i=0;i < foundUser.cart.length;i++){
                itemToPush = {
                    "name": foundUser.cart[i].name,
                    "sku": foundUser.cart[i].length,
                    "price": foundUser.cart[i].price,
                    "currency": "USD",
                    "quantity": 1
                }
                newItems.push(itemToPush);
            }
            //totalprice
            var totalPrice = 0;
            foundUser.cart.forEach(function(item){
                    totalPrice += item.price;  
                    return totalPrice;
            }); 
            res.redirect("back");
            // create paypal payment JSON
            var create_payment_json = {
                "intent": "sale",
                "payer": {
                    "payment_method": "paypal"
                },
                "redirect_urls": {
                    "return_url": "http://127.0.0.1:3000/shop",
                    "cancel_url": "http://127.0.0.1:3000/shop"
                },
                "transactions": [{
                    "item_list": {
                        "items": newItems
                    },
                    "amount": {
                        "currency": "USD",
                        "total": totalPrice
                    },
                    "description": "This is the payment description."
                }]
            };
            // console.log(JSON.stringify(create_payment_json));
            paypal.payment.create(create_payment_json, function(err, payment){
                if(err){
                    throw err;
                }else{
                    console.log(payment);
                    for(var i=0;i < payment.links.length;i++){
                        if(payment.links[i].rel === 'approval_url'){
                            console.log("FOUND " + payment.links[i].href);
                            res.redirect(payment.links[i].href);
                        }
                    }
                }
            });
        }
    });
});

module.exports = router;

Here the problem I get good status code but then this error:

(node:14648) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.

(node:14648) DeprecationWarning: current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.

Server started! { id: 'PAYID-L3AZRLQ4LA763246G079113E', intent: 'sale', state: 'created', payer: { payment_method: 'paypal' }, transactions: [ { amount: [Object], description: 'This is the payment description.', item_list: [Object], related_resources: [] } ], create_time: '2020-05-17T20:03:57Z', links: [ { href: 'https://api.sandbox.paypal.com/v1/payments/payment/PAYID-L3AZRLQ4LA763246G079113E', rel: 'self', method: 'GET' }, { href: 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-2C992495UH4895047', rel: 'approval_url', method: 'REDIRECT' }, { href: 'https://api.sandbox.paypal.com/v1/payments/payment/PAYID-L3AZRLQ4LA763246G079113E/execute', rel: 'execute', method: 'POST' } ], httpStatusCode: 201 } FOUND https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-2C992495UH4895047 _http_outgoing.js:526 throw new ERR_HTTP_HEADERS_SENT('set'); ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:526:11) at ServerResponse.header (C:\Users\Zohar Banai\Desktop\personal projects\ShopProject\node_modules\express\lib\response.js:771:10) at ServerResponse.location (C:\Users\Zohar Banai\Desktop\personal projects\ShopProject\node_modules\express\lib\response.js:888:15) at ServerResponse.redirect (C:\Users\Zohar Banai\Desktop\personal projects\ShopProject\node_modules\express\lib\response.js:926:18) at C:\Users\Zohar Banai\Desktop\personal projects\ShopProject\routes\cart.js:116:33 at IncomingMessage. (C:\Users\Zohar Banai\Desktop\personal projects\ShopProject\node_modules\paypal-rest-sdk\lib\client.js:140:13) at IncomingMessage.emit (events.js:323:22) at endReadableNT (_stream_readable.js:1204:12) at processTicksAndRejections (internal/process/task_queues.js:84:21) { code: 'ERR_HTTP_HEADERS_SENT' }

1

1 Answers

0
votes

Some pointers:

  • A redirect with the HTTP header 'Location: newurl' cannot be done if the HTTP headers have already been sent somewhere earlier in your application's response to the request. But, it isn't worth spending time fixing this issue -- see the below instead.

  • Redirects are the old way of doing things. Don't use any redirects. At all. At all at all. Instead, use a modern "in context" flow: keep your site loaded in the background and use this front-end: https://developer.paypal.com/demo/checkout/#/pattern/server (that's the server integration pattern; for a demo of what the UI will look like, click over to the client side pattern since that one is interactively clickable)

  • PAYIDs are from PayPal's old v1/payments REST API. Don't use v1/payments in your backend. Instead, change your server-side API calls to be v2/checkout/orders. If you want an SDK, here are the latest ones: https://developer.paypal.com/docs/api/rest-sdks/