Intergrations
Lets now intergrate our payment for to call the server actions.
Lets begin by advancing our handleSubmit
function to validate the formData and call the stkPush server action.
Validation and Calling STK PUSH
action
//add this at the top of the components and make sure your have useState imported
const [loading, setLoading] = useState <boolean> (false);
const handleSubmit = async () => {
setLoading(true);
const formData = {
mpesa_number: dataFromForm.mpesa_phone.trim(),
name: dataFromForm.name.trim(),
amount: dataFromForm.amount,
};
//validate as you wish - we just just validate the phone number for now to allow any mpesa number format
const kenyanPhoneNumberRegex =
/^(07\d{8}|01\d{8}|2547\d{8}|2541\d{8}|\+2547\d{8}|\+2541\d{8})$/;
if (!kenyanPhoneNumberRegex.test(formData.mpesa_number)) {
setLoading(false);
return alert("Invalid mpesa number");
}
const { data: stkData, error: stkError } = await sendStkPush(formData);
if (stkError) {
setLoading(false);
return alert(stkError);
}
const checkoutRequestId = stkData.CheckoutRequestID;
console.log(checkoutRequestId)
alert("stk push sent successfully");
};
Dont forget to import the sendStkPush
server action.
import { sendStkPush } from "@/actions/stkPush";
Lets also update our submit button to handle the loading
<button
type="submit"
onClick={handleSubmit}
disabled={loading}
className="inline-flex w-full items-center justify-center rounded-md border border-transparent bg-orange-500 px-4 py-4 text-base font-semibold text-white transition-all duration-200 hover:bg-orange-600 focus:bg-orange-600 focus:outline-none"
>
{loading ? "Processing.." : "Proceed With Payment"}
</button>;
Hurray!! You have successfully sent your First STK push.
Polling Transaction status
The function above returns a sucess when an stk push
is sent to the users device successfully.
We need a way to now check for the users interaction with the stk push popup.
To achieve this, we have a couple of options depending on the complexity of your project. In this guide we will use short polling
- a techique that will allow use to make request to the stkquery api at a certain interval to check for the stkpush status and dispay relevant feedback on the UI.
Lets start by adding the stk query logic in our paymentForm component. We will utilize javascripts setInterval
to achieve this.
//add this just before the handleSubmit Function
var reqcount = 0;
const stkPushQueryWithIntervals = (CheckoutRequestID: string) => {
const timer = setInterval(async () => {
reqcount += 1;
if (reqcount === 15) {
//handle long payment
clearInterval(timer);
setStkQueryLoading(false);
setLoading(false);
setErrorMessage("You took too long to pay");
}
const { data, error } = await stkPushQuery(CheckoutRequestID);
if (error) {
if (error.response.data.errorCode !== "500.001.1001") {
setStkQueryLoading(false);
setLoading(false);
setErrorMessage(error?.response?.data?.errorMessage);
}
}
if (data) {
if (data.ResultCode === "0") {
clearInterval(timer);
setStkQueryLoading(false);
setLoading(false);
setSuccess(true);
} else {
clearInterval(timer);
setStkQueryLoading(false);
setLoading(false);
setErrorMessage(data?.ResultDesc);
}
}
}, 2000);
};
const handleSubmit = async () => {
// ...... rest of the code from before
}
The stkPushQueryWithIntervals
function is designed to repeatedly check the status of an STK push transaction at regular intervals:
Initialization:
- A variable
reqcount
is initialized to keep track of the number of requests made.
Set Interval:
setInterval
is used to create a timer that runs the function every 2 seconds.
Request Count Check:
- If
reqcount
reaches 15 (indicating a long payment process), the timer is cleared, and an error message is displayed to the user.
STK Query API Call:
- The function makes a call to the
stkPushQuery
API with theCheckoutRequestID
. - If an error occurs and it's not a specific known error (
500.001.1001
), the timer is cleared, and the error message is displayed.
Response Handling:
- If the API returns a success response (
ResultCode
is "0"), the timer is cleared, and a success message is displayed. - If the API returns a failure response, the timer is cleared, and the error message is displayed.
Error Handling:
- Appropriate messages are set based on the success or failure of the transaction.
This logic ensures that the UI is updated in real-time based on the user's interaction with the STK push popup.
Dont forget to import the stkPushQuery
server action.
import { stkPushQuery } from "@/actions/stkPushQuery";
Lets now add the neccessary pieces of state.
//add these below the loading usestate
const [success, setSuccess] = useState<boolean>(false);
const [stkQueryLoading, setStkQueryLoading] = useState<boolean>(false);
To visually show the loading checkout proceses, we will now create two more componets that will dispay a loading screen
and a success screen
inside our components folder
Loading indicator
export default function STKPushQueryLoading({number}:{number:string}) {
return (
<div className="space-y-2 text-center text-black p-10 bg-gray-100">
<h1 className="animate-pulse">PROCESSING PAYMENT..</h1>
<h1>Stk push sent to {number}</h1>
<h1>Enter Pin to confirm payment</h1>
</div>
);
}
Payment successfull indicator
export default function PaymentSuccess() {
return (
<div className="space-y-2 text-center text-black p-10 bg-gray-100">
<h1>Your Payment was processed succesfully</h1>
<h1>Thank You for your Donation</h1>
</div>
);
}
Last thing, lets now render the sections based on our flow
// import the components at the top of the payment form component
import PaymentSuccess from "./Success";
import STKPushQueryLoading from "./StkQueryLoading";
//add the following conditional rendering for your code
function PaymentForm() {
...rest of the code
return (
<>
{stkQueryLoading ? (
<STKPushQueryLoading number={dataFromForm.mpesa_phone}/>
) : success ? (
<PaymentSuccess />
) : (
<div className="lg:pl-12"
...rest of the code
</div>
)}
</>
) }
One last thing, lets call the stkPushQueryWithIntervals
after an stk push is sent successfully.
const handleSubmit = async () => {
// ...... rest of the code from before
//you can remove the console log and the alert
setStkQueryLoading(true)
stkPushQueryWithIntervals(checkoutRequestId);
}
At this point, Our stk push and stk query logic should be working perfect as expected