1

I'm making payment function for my static e-commerce Next.js app.

For payment I've several stages:

  1. Making cart page with shipping information form and button "Pay" which redirect to /payment page;
  2. On /payment page I connect with my payment service and need to get cart info from Context API, but I can't use Context API in getStaticProps it's my problem. Payment page needs just get cart data and redirects on external service payment form.

Code for page /payment is below:

import { useEffect, useContext } from "react"
import QiwiBillPaymentsAPI from "@qiwi/bill-payments-node-js-sdk"

import { CartContext } from "@/context/GlobalState"

export default function Payment ({ payUrl }) {
    useEffect(() => window.location.assign(payUrl))
    return (
        <span>Redirect</span>
    )
}


export async function getStaticProps() {
    const qiwiApi = new QiwiBillPaymentsAPI(process.env.QIWI_SECRET_KEY)

    const { state, dispatch } = useContext(CartContext)
    const { cart } = state

    const billId = qiwiApi.generateId()
    const lifetime = qiwiApi.getLifetimeByDay(1);
    const fields = {
        amount: 1.00,
        currency: "RUB",
        expirationDateTime: lifetime,
    }

    const payment_data = await qiwiApi.createBill( billId, fields )
    const payUrl = payment_data.payUrl

    return { props: { payUrl }}
}

Please, help me with any ideas.

juliomalves
  • 42,130
  • 20
  • 150
  • 146
andrewnosov
  • 374
  • 4
  • 14
  • 2
    `getStaticProps` runs at build-time on the server. You won't have access to any runtime or client-side logic in there. Also, why do you need access to the context if you don't seem to be using the cart data in `getStaticProps`? Couldn't you handle the context in your `Payment` component instead? – juliomalves Mar 13 '21 at 18:26
  • Just one thing I had noticed from your code, you are using `window.location.assign`, which is probably a good idea in this case. But I'm afraid of you are using the same thing for everything. Make sure you use `next router`. Otherwise you are completely opt yourself out from SPA and advantage of using `Next.js` – Yunhai Mar 13 '21 at 19:16
  • @Yunhai Thanks for your attention! Yes, I'm using `window.location.assign` only in this case. And I was looking for approach to make it with `next router`, but I can't. If you know, tell me please! – andrewnosov Mar 13 '21 at 20:15
  • @juliomalves I need to have context api access in `getStaticProps` for change `amount` (total price for future payment) in fields object and something more. For some reason I cannot use qiwiApi inside of the Payment component, I tried it in useEffect or just in function but in don't work and returns error with 'fs'. – andrewnosov Mar 13 '21 at 20:21
  • 3
    I'd recommend you move the `getStaticProps` code to an API route, then make a request from your `Payment` component to that API route with whatever you need from the context. – juliomalves Mar 13 '21 at 20:41
  • related to [Nextjs and Context API](https://stackoverflow.com/questions/54127650/nextjs-and-context-api) – Axel Anaya Mar 14 '21 at 07:12
  • @juliomalves I've add my solution (like you said). Could you check, please? – andrewnosov Mar 14 '21 at 09:30

1 Answers1

2

Possible solution!

I've made API Route with code from my getStaticProps function (it gets total price and make query to payment service, then it return payment url). Code of api route is below:

import QiwiBillPaymentsAPI from "@qiwi/bill-payments-node-js-sdk"

export default async function handler(req, res) {
    const { 
        query: { total },
    } = req

    const qiwiApi = new QiwiBillPaymentsAPI(process.env.QIWI_SECRET_KEY)
    const billId = qiwiApi.generateId()
    const lifetime = qiwiApi.getLifetimeByDay(1);
    const fields = {
        amount: total,
        currency: "RUB",
        expirationDateTime: lifetime,
    }

    const payment_data = await qiwiApi.createBill( billId, fields )
    const payUrl = payment_data.payUrl

    res.json({ url: payUrl })
}

Then /payment page code (do query to my API route and get payment url, then redirect to payment form):

export default function Payment () {
    const { state, dispatch } = useContext(CartContext)
    const { cart } = state

    const fetcher = (...args) => fetch(...args).then(res => res.json())

    const { data, error } = useSWR(`/api/qiwi-pay?total=${cart.total}`, fetcher)

    if (error) return <div>failed to load</div>
    if (!data) return <div>loading...</div>

    window.location.assign(data.url)

    return (
        <>
            <span>Redirect to payment form.</span>
        </>
    )
}

This approach works very well!

andrewnosov
  • 374
  • 4
  • 14