đź§© Coding Challenge: Build a Maya Checkout Integration (Debug Edition)

Hey developers :waving_hand:

We’re sharing a sample Node.js Maya Checkout integration… BUT something isn’t quite right.

The implementation looks mostly correct, yet the Checkout request still fails unexpectedly.

Your mission: Find what’s wrong in the implementation

  • Debug the integration
  • Identify up to 5 subtle bugs
  • Explain what’s wrong and how you would fix it

This challenge is designed to simulate real-world integration debugging where everything looks correct… until it isn’t.


Starter Code

.env

MAYA_CHECKOUT_URL=https://pg-sandbox.paymaya.com/checkout/v1/checkouts
MAYA_PUBLIC_API_KEY=pk-Z0OSzLvIcOI2UIvDhdTGVVfRSSeiGStnceqwUE7n0Ah
REDIRECT_URL=http://localhost:3000/checkout/success

app.ts

import fetch from "node-fetch"
import dotenv from "dotenv"
import { v4 as uuidv4 } from "uuid"

dotenv.config()

const MAYA_CHECKOUT_URL = process.env.MAYA_CHECKOUT_URL
const PUB_KEY = process.env.MAYA_PUBLIC_API_KEY
const SEC_KEY = process.env.MAYA_SECRET_API_KEY

export const createCheckout = async (cart, buyer) => {
  const requestReferenceNumber = uuidv4()

  // Generate auth token
  const authToken = Buffer
    .from(`${PUB_KEY}:${SEC_KEY}`, "base64")
    .toString("utf8")

  const payload = {
    totalAmount: {
      currency: "PHP",
      value: cart.total
    },

    items: cart.items.map(item => ({
      name: item.name,
      quantity: item.qty,
      amount: {
        value: item.price
      }
    })),

    buyer: {
      firstName: buyer.firstName,
      lastName: buyer.lastName,
      contact: {
        email: buyer.email
      }
    },

    redirectUrl: process.env.REDIRECT_URL,

    requestReferenceNumber
  }

  const response = await fetch(`${MAYA_CHECKOUT_URL}/checkouts`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Basic ${authToken}`,
      "X-Request-Id": requestReferenceNumber
    },
    body: JSON.stringify(payload)
  })

  return response.json()
}

:brain: Challenge Mechanics

  • There are multiple hidden bugs in the implementation.
  • To keep the discussion collaborative, each developer may only claim and explain ONE bug per reply/comment.
  • Before posting, check if another developer has already identified the same issue
  • If your bug was already mentioned, try:
    • finding another hidden issue
    • challenging the proposed fix
    • or suggesting a different/improved approach to solving it
  • Bonus points for:
    • explaining why the issue happens
    • suggesting production-ready fixes
    • identifying edge cases or long-term risks
    • sharing debugging techniques or tooling used

This isn’t just about spotting bugs, it’s also about discussing how different developers approach integration problems in real-world scenarios


:pencil: What to Reply

Reply with:

  1. Bugs you found
  2. Why they’re problematic
  3. How to fix them
  4. (Optional) improved working implementation

:bullseye: Community Goal

Let’s see how different developers approach debugging the same integration.

3 Likes

In the Generate auth token of the sample code provided:

  • It provides the public and secret key, where the Create Checkout endpoint requires the public key only
  • base64 was used as the input encoding, which is wrong here

I think the fix is the same with your feedback on my other thread. To resolve it:

// Generate auth token
  const authToken = Buffer
    .from(`${PUB_KEY}:`, "utf-8")
    .toString("base64")

To define the authToken value, build the string first in UTF-8, then encode it to base64.

1 Like

Hi,

Hmm…

One thing I found that may lead to unexpected error is the value of MAYA_CHECKOUT_URL environment variable. It has /checkouts path already, but on the source code it is appended too.

const response = await fetch(`${MAYA_CHECKOUT_URL}/checkouts` <<-- possible issue, { ... })

This issue may prevent consumer to process payment thru checkout page.

To prevent this, (1) you may remove the /checkouts path from the value of MAYA_CHECKOUT_URL or (2) remove the /checkouts path from the source code

const response = await fetch(`${MAYA_CHECKOUT_URL}/checkouts` <<-- remove this path, { ... })

That’s it. Let me know if this is one of the bugs :grin:

3 Likes

Hi,

From the first reply/comment, secret api key is not needed. For security, we must remove unused sensitive data like this.

3 Likes

Seems like items mapping only included the amount, which is the cost per item only, not including total price for multiple quantities of the same item.

I think this should include totalAmount to account for multiple quantities, can be written as price Ă— quantity

Something like:

totalAmount: { value: item.price * item.qty }
1 Like

Need to add await for getting the response and added handling of error

const response = await fetch(…)

if (!response.ok) {

const error = await response.json() throw new Error(`Maya API ${response.status}: ${error.message}`)

}

return response.json()

1 Like

:tada: That’s a wrap, everyone!

Great job to everyone who participated in the first Coding Challenge. We’re officially closing the challenge today.

We hope this mini challenge helped reinforce some of the fundamentals of integrating with Maya Checkout and highlighted a few common pitfalls that developers may encounter during implementation.

Key Takeaways

1. Use the correct API key and format it properly

For the Create Checkout endpoint, only the Public API Key is required for authentication. Make sure the key is correctly formatted and Base64-encoded before sending the request.

const PUB_KEY = process.env.MAYA_PUBLIC_API_KEY

const authToken = Buffer
  .from(`${PUB_KEY}:`, "utf-8")
  .toString("base64")

2. Verify your endpoint path

Small mistakes in URLs and path parameters can lead to unexpected errors. Always double-check the endpoint defined in the API specifications.

MAYA_CHECKOUT_URL=https://pg-sandbox.paymaya.com/checkout/v1/checkouts
const response = await fetch(MAYA_CHECKOUT_URL, {
  ...
})

3. Validate required request parameters

Always refer to the API documentation and ensure all required fields are present. In this challenge, items.totalAmount was expected in the payload.

items: cart.items.map(item => ({
  name: item.name,
  quantity: item.qty,
  amount: {
    value: item.price
  },
  totalAmount: {
    value: item.price * item.qty
  }
}))

Also remember to derive amount values correctly to avoid mismatches between item totals and transaction totals.

4. Handle API errors gracefully

A successful HTTP request doesn’t always mean a successful API transaction. Always check the response status and surface meaningful errors.

const response = await fetch(...)

if (!response.ok) {
  const error = await response.json()
  throw new Error(
    `Maya API ${response.status}: ${error.message}`
  )
}

return response.json()

5. Make your request references unique and traceable

Unique request reference numbers make troubleshooting, reconciliation, and support investigations much easier.

const requestReferenceNumber = uuidv4()

const payload = {
  ...req,
  requestReferenceNumber
}

While we’ve identified all the intended bugs for this challenge, that doesn’t necessarily mean the sample code is production-ready.

If you still spot additional bugs, edge cases, security concerns, performance improvements, or opportunities to refine the implementation, feel free to continue the discussion in the replies. We’d love to see alternative approaches and learn how other developers would strengthen the integration beyond the scope of this challenge.


:raising_hands: Thank You!

Thank you to everyone who joined the challenge, shared fixes, proposed alternative approaches, and challenged each other’s solutions. The discussions were just as valuable as the bug discoveries themselves.

We’d love to hear your thoughts:

  • Was the challenge fun and engaging?
  • Was the difficulty level appropriate?
  • What topics or products would you like to see in future challenges?

Feel free to share your feedback in Site Feedback or through our survey.

See you in the next challenge! :rocket: