craftgate/craftgate-node-client

Payment requests returning 403 in Next.js app

enesozturk opened this issue · 3 comments

Hi, I was just testing Craftbase with my sandbox credentials. I am using it in my Next JS application same with example in readme file. Request returning 403 and I did not understand why it could be? Other details below;

I see two request in Network tab;
Screen Shot 2021-07-11 at 23 57 44

First request details
Request URL: https://sandbox-api.craftgate.io/payment/v1/card-payments
Referrer Policy: strict-origin-when-cross-origin
Provisional headers are shown
Accept: application/json, text/plain, */*
Content-Type: application/json
Referer: http://localhost:3000/
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36
x-api-key: {SAME_WITH_MINE}
x-auth-version: 1
x-rnd-key: LRLyIQ
x-signature: uLI/jBZ4F6SFqvIkxRYb/cC2uWYY+A0tHcmWBZdpo6I=
{price: 100, paidPrice: 100, walletPrice: 0, installment: 1,}
card: {cardHolderName: "Haluk Demir", cardNumber: "5258640000000001", expireYear: "2044", expireMonth: "07",}
conversationId: "456d1297-908e-4bd6-a13b-4be31a6e47d5"
currency: "TRY"
installment: 1
items: [{name: "Item 1", price: 30, externalId: "123d1297-839e-4bd6-a13b-4be31a6e12a8"},]
paidPrice: 100
paymentGroup: "LISTING_OR_SUBSCRIPTION"
price: 100
walletPrice: 0
Second request details
Request URL: https://sandbox-api.craftgate.io/payment/v1/card-payments
Request Method: OPTIONS
Status Code: 403 
Remote Address: 52.29.157.220:443
Referrer Policy: strict-origin-when-cross-origin
content-length: 0
date: Sun, 11 Jul 2021 20:57:39 GMT
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
:authority: sandbox-api.craftgate.io
:method: OPTIONS
:path: /payment/v1/card-payments
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7
access-control-request-headers: content-type,x-api-key,x-auth-version,x-rnd-key,x-signature
access-control-request-method: POST
origin: http://localhost:3000
referer: http://localhost:3000/
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36
Code Snippets

Codes are same with document as I said but may be good to share:

const Craftgate = require("@craftgate/craftgate");

const craftgate = new Craftgate.Client({
  apiKey: "{MY_CRAFBASE_API_KEY}",
  secretKey: "{MY_CRAFBASE_SECRET_KEY}",
  baseUrl: "https://sandbox-api.craftgate.io",
});

var request = {
  price: 100.0,
  paidPrice: 100.0,
  walletPrice: 0.0,
  installment: 1,
  conversationId: "456d1297-908e-4bd6-a13b-4be31a6e47d5",
  currency: Craftgate.Model.Currency.TRY,
  paymentGroup: Craftgate.Model.PaymentGroup.ListingOrSubscription,
  card: {...},
  items: [...],
};

const handleCreatePayment = () => {
    craftgate
      .payment()
      .createPayment(request)
      .then(function (result) {
        console.info("Payment successful", result);
      })
      .catch(function (err) {
        console.error("Payment failed", err);
      });
  };

Versions

Next.js: 10.1.3
React: 17.0.2
Node JS: v14.17.0

Hi there, it seems like the request went through the browser and was subsequently blocked by the CORS process, as indicated by the failed OPTIONS request. We use specific CORS headers to prevent browsers from making AJAX requests to our APIs because we'd like to discourage this usage scenario for security reasons. Otherwise the integrator (which, in this case, is you) would have to embed their API secrets in their client-side JavaScript code, which in turn would allow any visitor to obtain those credentials and make payment requests as if they were the said integrator.

There are ways to make AJAX calls from browsers and have them bypass the CORS process, but since it still requires you to expose your API secrets we highly discourage using them. You can read more about CORS here: https://auth0.com/blog/cors-tutorial-a-guide-to-cross-origin-resource-sharing/

The most common approach for this use case is to implement your own backend server, protect it with some kind of authentication mechanism, implement specific endpoints for specific purposes depending on your business rules, and access the Craftgate API through this backend server, making sure no API secrets are leaked publicly.

I don't have any experience in Next.js but according to this page you should be able to implement an API endpoint in your Next.js app: https://nextjs.org/docs/api-routes/introduction

Can you please try this approach and see if it works for you?

Thanks for explanations and details, I will check and share results with you as soon as possible.

It is solved 👌🏽 As you mentioned above, I needed to create my own API end point to call your end points.

For the future Next.js followers:

Instead of calling Crafgate end points directly inside the front end side, try creating and API route for your app in /api folder. These routes will be on just server side and you will be able to call them on front end side.

/api/payment.ts:

const Craftgate = require("@craftgate/craftgate");

const craftgate = new Craftgate.Client({
  apiKey: "...",
  secretKey: "...",
  baseUrl: "https://sandbox-api.craftgate.io",
});

var request = { /* Check Craftgate docs */}

export default (req, res) => {
  craftgate
    .payment()
    .createPayment(request)
    .then(function (result) {
      res.status(200).json({ message: "Payment successful" });
    })
    .catch(function (err) {
      throw Error(err);
    });
};

/pages/checkout.tsx:

const handleCreatePayment = () => {
    fetch("/api/payment")
      .then(() => {})
      .catch(() => {});
  };

<button onClick={handleCreatePayment}>Proceed Checkout</button>