Skip to content

Commit

Permalink
payment notification done
Browse files Browse the repository at this point in the history
  • Loading branch information
fachrihawari committed Sep 26, 2024
1 parent 6be5714 commit 4b2c3f3
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 16 deletions.
14 changes: 4 additions & 10 deletions src/app/api/orders/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import { ObjectId } from "mongodb";
import { OrderForm, OrderSchema, ordersCollection } from "@/lib/db/order_collection";
import { NextRequest, NextResponse } from "next/server";
import { cartsCollection } from "@/lib/db/cart_collection";
import midtransClient from "midtrans-client";
import { usersCollection } from "@/lib/db/user_collection";

import midtransSnap from "@/lib/payment/midtrans";
export async function GET(req: NextRequest) {
const userId = String(req.headers.get("x-user-id"))
const orders = await ordersCollection.find({ userId: new ObjectId(userId) }).toArray()
Expand Down Expand Up @@ -40,13 +39,6 @@ export async function POST(req: NextRequest) {
// Insert the order into the database
const result = await ordersCollection.insertOne(newOrder);

// Prepare Midtrans payment request
const snap = new midtransClient.Snap({
isProduction: process.env.NODE_ENV === "production",
serverKey: process.env.MIDTRANS_SERVER_KEY,
clientKey: process.env.MIDTRANS_CLIENT_KEY
});

const transactionDetails = {
order_id: result.insertedId.toString(),
gross_amount: newOrder.total
Expand All @@ -71,7 +63,9 @@ export async function POST(req: NextRequest) {
};

// Create Midtrans transaction
const transaction = await snap.createTransaction(midtransParameter);
const transaction = await midtransSnap.createTransaction(midtransParameter);

await ordersCollection.updateOne({ _id: result.insertedId }, { $set: { payment: { token: transaction.token } } })

// Return the Midtrans token
return NextResponse.json({
Expand Down
Empty file.
52 changes: 52 additions & 0 deletions src/app/api/payment/notification/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { NextRequest, NextResponse } from "next/server";
import midtransSnap from "@/lib/payment/midtrans";
import { MidtransNotification, Order, ordersCollection } from "@/lib/db/order_collection";
import { ObjectId } from "mongodb";

export async function POST(req: NextRequest) {
try {
const body = await req.json();

const response = await midtransSnap.transaction.notification(body);
const { order_id: orderId, transaction_status: transactionStatus, fraud_status: fraudStatus } = response;

console.log(`Transaction notification received. Order ID: ${orderId}. Transaction status: ${transactionStatus}. Fraud status: ${fraudStatus}`);

const orderStatus = getOrderStatus(transactionStatus, fraudStatus);

await updateOrderStatus(orderId, orderStatus, body);

return NextResponse.json({ message: 'OK' }, { status: 200 });
} catch (error) {
console.error('Error processing payment notification:', error);
return NextResponse.json({ message: 'Internal Server Error' }, { status: 500 });
}
}

function getOrderStatus(transactionStatus: string, fraudStatus: string): Order['status'] {
if ((transactionStatus === 'capture' && fraudStatus === 'accept') || transactionStatus === 'settlement') {
return 'paid';
} else if (['cancel', 'deny', 'expire'].includes(transactionStatus)) {
return 'cancelled';
} else if (transactionStatus === 'pending') {
return 'pending';
}

throw new Error(`Unknown transaction status: ${transactionStatus}`);
}

async function updateOrderStatus(orderId: string, status: Order['status'], notificationBody: MidtransNotification) {
const result = await ordersCollection.updateOne(
{ _id: new ObjectId(orderId) },
{
$set: {
status,
"payment.notification": notificationBody
}
}
);

if (result.matchedCount === 0) {
throw new Error(`Order not found: ${orderId}`);
}
}
4 changes: 0 additions & 4 deletions src/lib/actions/orders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import { redirect } from "next/navigation";
const NEXT_PUBLIC_URL = process.env.NEXT_PUBLIC_URL

export const checkout = async () => {
await new Promise(resolve => setTimeout(resolve, 3000))
return {
paymentToken: "f39dbe10-2dc5-4b41-8359-a17d6dc2e5eb"
}

const res = await fetch(`${NEXT_PUBLIC_URL}/api/orders`, {
method: 'POST',
Expand Down
5 changes: 3 additions & 2 deletions src/lib/db/order_collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { z } from "zod";
import { db } from "./client";
import { CartItemSchema } from "./cart_collection";

const NotificationSchema = z.object({
export const MidtransNotificationSchema = z.object({
transaction_time: z.string(),
transaction_status: z.string(),
transaction_id: z.string(),
Expand All @@ -27,7 +27,7 @@ const NotificationSchema = z.object({

export const PaymentSchema = z.object({
token: z.string().optional(),
notification: NotificationSchema.optional()
notification: MidtransNotificationSchema.optional()
})

export const OrderSchema = z.object({
Expand All @@ -40,6 +40,7 @@ export const OrderSchema = z.object({
updatedAt: z.date().default(new Date()).optional(),
});

export type MidtransNotification = z.infer<typeof MidtransNotificationSchema>
export type OrderForm = z.infer<typeof OrderSchema>
export type Order = WithId<OrderForm>

Expand Down
11 changes: 11 additions & 0 deletions src/lib/payment/midtrans.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

import midtransClient from "midtrans-client";

// Prepare Midtrans payment request
const midtransSnap = new midtransClient.Snap({
isProduction: process.env.NODE_ENV === "production",
serverKey: process.env.MIDTRANS_SERVER_KEY,
clientKey: process.env.MIDTRANS_CLIENT_KEY
});

export default midtransSnap

0 comments on commit 4b2c3f3

Please sign in to comment.