Skip to content

Commit

Permalink
working app , finish payment
Browse files Browse the repository at this point in the history
  • Loading branch information
Satora1 committed Oct 28, 2024
1 parent 4a62d85 commit 3687a38
Show file tree
Hide file tree
Showing 9 changed files with 409 additions and 88 deletions.
53 changes: 53 additions & 0 deletions app/(api)/(stripe)/create+api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Stripe } from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(request: Request) {
const body = await request.json();
const { name, email, amount } = body;

if (!name || !email || !amount) {
return new Response(JSON.stringify({ error: "Missing required fields" }), {
status: 400,
});
}

let customer;
const doesCustomerExist = await stripe.customers.list({
email,
});

if (doesCustomerExist.data.length > 0) {
customer = doesCustomerExist.data[0];
} else {
const newCustomer = await stripe.customers.create({
name,
email,
});

customer = newCustomer;
}

const ephemeralKey = await stripe.ephemeralKeys.create(
{ customer: customer.id },
{ apiVersion: "2024-06-20" },
);

const paymentIntent = await stripe.paymentIntents.create({
amount: parseInt(amount) * 100,
currency: "usd",
customer: customer.id,
automatic_payment_methods: {
enabled: true,
allow_redirects: "never",
},
});

return new Response(
JSON.stringify({
paymentIntent: paymentIntent,
ephemeralKey: ephemeralKey,
customer: customer.id,
}),
);
}
37 changes: 37 additions & 0 deletions app/(api)/(stripe)/pay+api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Stripe } from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(request: Request) {
try {
const body = await request.json();
const { payment_method_id, payment_intent_id, customer_id } = body;

if (!payment_method_id || !payment_intent_id || !customer_id) {
return new Response(JSON.stringify({ error: "Missing required fields for payment" }), {
status: 400,
});
}
const paymentMethod = await stripe.paymentMethods.attach(payment_method_id, {
customer: customer_id
})
const result = await stripe.paymentIntents.confirm(payment_intent_id, {
payment_method: paymentMethod.id
})
return new Response(
JSON.stringify({
success: true,
message: "Payment Confirm succesfull",
result: result
})
)
} catch (error) {
console.log(error)
return new Response(
JSON.stringify({
error: error,
status: 500
})
)
}
}
13 changes: 13 additions & 0 deletions app/(api)/driver+api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { data } from "@/constants"
import { neon } from "@neondatabase/serverless"

export async function GET() {
try {
const sql = neon(`${process.env.DATABASE_URL}`)
const response = await sql`SELECT*FROM drivers`
return Response.json({ data :response})
} catch (error) {
console.log(error)
return Response.json({ error: error })
}
}
46 changes: 46 additions & 0 deletions app/(api)/ride/[id]+api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {neon} from "@neondatabase/serverless";

export async function GET(request: Request, {id}: { id: string }) {
if (!id)
return Response.json({error: "Missing required fields"}, {status: 400});

try {
const sql = neon(`${process.env.DATABASE_URL}`);
const response = await sql`
SELECT
rides.ride_id,
rides.origin_address,
rides.destination_address,
rides.origin_latitude,
rides.origin_longitude,
rides.destination_latitude,
rides.destination_longitude,
rides.ride_time,
rides.fare_price,
rides.payment_status,
rides.created_at,
'driver', json_build_object(
'driver_id', drivers.id,
'first_name', drivers.first_name,
'last_name', drivers.last_name,
'profile_image_url', drivers.profile_image_url,
'car_image_url', drivers.car_image_url,
'car_seats', drivers.car_seats,
'rating', drivers.rating
) AS driver
FROM
rides
INNER JOIN
drivers ON rides.driver_id = drivers.id
WHERE
rides.user_id = ${id}
ORDER BY
rides.created_at DESC;
`;

return Response.json({data: response});
} catch (error) {
console.error("Error fetching recent rides:", error);
return Response.json({error: "Internal Server Error"}, {status: 500});
}
}
75 changes: 75 additions & 0 deletions app/(api)/ride/create+api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {neon} from "@neondatabase/serverless";

export async function POST(request: Request) {
try {
const body = await request.json();
const {
origin_address,
destination_address,
origin_latitude,
origin_longitude,
destination_latitude,
destination_longitude,
ride_time,
fare_price,
payment_status,
driver_id,
user_id,
} = body;

if (
!origin_address ||
!destination_address ||
!origin_latitude ||
!origin_longitude ||
!destination_latitude ||
!destination_longitude ||
!ride_time ||
!fare_price ||
!payment_status ||
!driver_id ||
!user_id
) {
return Response.json(
{error: "Missing required fields"},
{status: 400},
);
}

const sql = neon(`${process.env.DATABASE_URL}`);

const response = await sql`
INSERT INTO rides (
origin_address,
destination_address,
origin_latitude,
origin_longitude,
destination_latitude,
destination_longitude,
ride_time,
fare_price,
payment_status,
driver_id,
user_id
) VALUES (
${origin_address},
${destination_address},
${origin_latitude},
${origin_longitude},
${destination_latitude},
${destination_longitude},
${ride_time},
${fare_price},
${payment_status},
${driver_id},
${user_id}
)
RETURNING *;
`;

return Response.json({data: response[0]}, {status: 201});
} catch (error) {
console.error("Error inserting data into recent_rides:", error);
return Response.json({error: "Internal Server Error"}, {status: 500});
}
}
10 changes: 8 additions & 2 deletions app/(root)/book-ride.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const BookRide = () => {
<View className="flex flex-row items-center justify-between w-full border-b border-white py-3">
<Text className="text-lg font-JakartaRegular">Pickup Time</Text>
<Text className="text-lg font-JakartaRegular">
{formatTime(driverDetails?.time!)}
{formatTime(parseInt(`${driverDetails.time!}`))}
</Text>
</View>

Expand All @@ -91,7 +91,13 @@ const BookRide = () => {
</Text>
</View>
</View>
<Payment />
<Payment
fullName={user?.fullName!}
email={user?.emailAddresses[0].emailAddress!}
amount={driverDetails?.price!}
driverId={driverDetails?.id}
rideTime={driverDetails?.time!}
/>
</>
</RideLayout>
</StripeProvider>
Expand Down
7 changes: 3 additions & 4 deletions components/DriverCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ const DriverCard = ({ item, selected, setSelected }: DriverCardProps) => {
return (
<TouchableOpacity
onPress={setSelected}
className={`${
selected === item.id ? "bg-general-600" : "bg-white"
} flex flex-row items-center justify-between py-5 px-3 rounded-xl`}
className={`${selected === item.id ? "bg-general-600" : "bg-white"
} flex flex-row items-center justify-between py-5 px-3 rounded-xl`}
>
<Image
source={{ uri: item.profile_image_url }}
Expand Down Expand Up @@ -42,7 +41,7 @@ const DriverCard = ({ item, selected, setSelected }: DriverCardProps) => {
</Text>

<Text className="text-sm font-JakartaRegular text-general-800">
{formatTime(item.time!)}
{formatTime(parseInt(`${item.time!}`))}
</Text>

<Text className="text-sm font-JakartaRegular text-general-800 mx-1">
Expand Down
82 changes: 66 additions & 16 deletions components/Payment.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useAuth } from "@clerk/clerk-expo";
import { useStripe } from "@stripe/stripe-react-native";
import { PaymentMethod, useStripe } from "@stripe/stripe-react-native";
import { router } from "expo-router";
import React, { useState } from "react";
import { Alert, Image, Text, View } from "react-native";
Expand All @@ -10,32 +10,82 @@ import { images } from "@/constants";
import { fetchAPI } from "@/lib/fetch";
import { useLocationStore } from "@/store";
import { PaymentProps } from "@/types/type";
import { shouldUseActivityState } from "react-native-screens";

const Payment = () => {
const { initPaymentSheet, presentPaymentSheet } = useStripe()
const [success, setSuccess] = useState(false)
const Payment = ({
fullName,
email,
amount,
driverId,
rideTime
}: PaymentProps) => {
const { initPaymentSheet, presentPaymentSheet } = useStripe();
const [success, setSuccess] = useState(false);

const confirmHandler = async (paymentMethod, _, intentCreationCallback) => {
const { paymentIntent, customer } = await fetchAPI(
"/(api)/(stripe)/create",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: fullName || email.split("@")[0],
email: email,
amount: amount,
PaymentMethod: paymentMethod.id,
}),
}
);

if (paymentIntent?.client_secret) {
const result = await fetchAPI("/(api)/(stripe)/pay", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
payment_method_id: paymentMethod.id,
payment_intent_id: paymentIntent.id,
customer_id: customer,
}),
});

if(result.client_secret){

}

const { clientSecret, error } = await result.json();
if (clientSecret) {
intentCreationCallback({ clientSecret });
} else {
intentCreationCallback({ error });
}
}
};

const initializePaymentSheet = async () => {
const { error } = await initPaymentSheet({
merchantDisplayName: "Example,Inc.",
merchantDisplayName: "Example, Inc.",
intentConfiguration: {
mode: {
amount: 1990,
currencyCode: "USD"
currencyCode: "USD",
},
confirmHandler: confirmHandler
}
})
confirmHandler: confirmHandler,
},
});

}
const confirmHandler = async () => {
if (error) {
Alert.alert("Payment initialization error", error.message);
}
};

}
const openPaymentSheet = async () => {
await initializePaymentSheet();

const { error } = await presentPaymentSheet();

if (error) {
Alert.alert(`Error code: ${error.code}`, error.message);
} else {
Expand All @@ -51,7 +101,7 @@ const Payment = () => {
onPress={openPaymentSheet}
/>
</>
)
}
);
};

export default Payment
export default Payment;
Loading

0 comments on commit 3687a38

Please sign in to comment.