Skip to content

Commit e763bae

Browse files
committed
Adds a separate form to pay for Coderplex
1 parent 8d44b3c commit e763bae

File tree

10 files changed

+103
-12
lines changed

10 files changed

+103
-12
lines changed

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,10 @@ typings/
8989
.dynamodb/
9090

9191
# package-lock
92-
package-lock.json
92+
package-lock.json
93+
94+
# Now
95+
.now
96+
97+
# Editor
98+
.idea

env.sample

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ RZP_LIVE_SECRET=
55
AIRTABLE_KEY=
66
AIRTABLE_BASE_ID=
77
AIRTABLE__DONATIONS_TABLE_NAME=Donations
8+
AIRTABLE__PAYMENTS_TABLE_NAME=Payments
89
AIRTABLE__CAMPAIGN_TABLE_NAME=Campaigns
9-
AIRTABLE__FUNDING_TABLE_NAME=Funding
10+
AIRTABLE__FUNDING_TABLE_NAME=Funding

now.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"AIRTABLE_KEY": "@airtable_key",
1212
"AIRTABLE_BASE_ID": "@airtable_base_id",
1313
"AIRTABLE__DONATIONS_TABLE_NAME": "@airtable__donations_table_name",
14+
"AIRTABLE__PAYMENTS_TABLE_NAME": "@airtable__payments_table_name",
1415
"AIRTABLE__FUNDING_TABLE_NAME": "@airtable__funding_table_name",
1516
"AIRTABLE__CAMPAIGN_TABLE_NAME": "@airtable__campaign_table_name"
1617
},

src/components/Home/index.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ export const HomeContent = () => {
3030
<span className="inline-block ml-2">Make A Donation</span>
3131
</Button>
3232
</div>
33+
<div className="p-4 flex justify-center">
34+
<Button href="/pay">
35+
<EmojiIcon />
36+
<span className="inline-block ml-2">Make A Payment</span>
37+
</Button>
38+
</div>
3339
</div>
3440
);
3541
};

src/components/common/PaymentForm.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import clsx from 'clsx';
1212
interface Props {
1313
campaign?: CampaignWithFundings;
1414
inlineForm?: boolean;
15+
isPayment?: boolean;
1516
}
1617

17-
export const PaymentForm: FunctionComponent<Props> = ({ campaign, inlineForm = false }) => {
18+
export const PaymentForm: FunctionComponent<Props> = ({ campaign, inlineForm = false, isPayment = false }) => {
1819
const maxAmount = campaign ? campaign.required_amount : Number.MAX_SAFE_INTEGER;
1920
const amount = 100 > maxAmount ? maxAmount : 100;
2021
const initialState = { amount, email: '', phone: '', name: '' };
@@ -76,6 +77,7 @@ export const PaymentForm: FunctionComponent<Props> = ({ campaign, inlineForm = f
7677
...form,
7778
amount: Number(form.amount),
7879
campaign: campaign ? campaign.slug : undefined,
80+
isPayment
7981
});
8082
setIsSubmitting(false);
8183
setFormValue(initialState);
@@ -163,7 +165,7 @@ export const PaymentForm: FunctionComponent<Props> = ({ campaign, inlineForm = f
163165
<div className="flex justify-center mb-3">
164166
<Button type="submit" isLoading={isSubmitting} disabled={isSubmitting}>
165167
<IconCards width={27} height={22} />
166-
<span className="ml-4 inline-block font-bold">Contribute ₹ {finalAmount.toFixed(2)}</span>
168+
<span className="ml-4 inline-block font-bold">{ isPayment ? 'Pay' : 'Contribute' }{finalAmount.toFixed(2)}</span>
167169
</Button>
168170
</div>
169171
<p className="text-xs text-gray-600 text-center">
@@ -180,7 +182,7 @@ export const PaymentForm: FunctionComponent<Props> = ({ campaign, inlineForm = f
180182
<PaymentSuccess />
181183
</div>
182184
<p className="text-center py-1 font-semibold text-xl text-pink-600">
183-
Thanks for your {campaign ? 'contribution' : 'donation'}.
185+
Thanks for your {campaign ? 'contribution' : isPayment ? 'payment' : 'donation'}.
184186
</p>
185187
<p className="text-center text-sm text-gray-600">We truly appreciate your generosity :D</p>
186188
</Modal>

src/pages/api/rzp-status.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import crypto from 'crypto';
22
import { NextApiRequest, NextApiResponse } from 'next';
33

4-
import { donationsBase, Views, fundingsBase, campaignsBase, PaymentStatus } from '../../services/airtable';
4+
import {
5+
donationsBase,
6+
paymentsBase,
7+
Views,
8+
fundingsBase,
9+
campaignsBase,
10+
PaymentStatus
11+
} from '../../services/airtable';
512

613
const RZP_SECRET = process.env.NODE_ENV === 'development' ? process.env.RZP_TEST_SECRET : process.env.RZP_LIVE_SECRET;
714

@@ -31,7 +38,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
3138
}
3239
}
3340

34-
async function updateStateInAirtable({ razorpay_order_id, razorpay_payment_id, status, campaign }) {
41+
async function updateStateInAirtable({ razorpay_order_id, razorpay_payment_id, status, campaign, isPayment }) {
3542
const query = {
3643
filterByFormula: `{order_id} = "${razorpay_order_id}"`,
3744
view: Views.grid,
@@ -70,11 +77,27 @@ async function updateStateInAirtable({ razorpay_order_id, razorpay_payment_id, s
7077
donations_count: result.fields.donations_count + 1,
7178
}),
7279
]);
80+
} else if(isPayment) {
81+
return updatePaymentBase({ razorpay_order_id, ...data })
7382
}
7483

7584
return updateDonationBase({ razorpay_order_id, ...data });
7685
}
7786

87+
async function updatePaymentBase({ razorpay_order_id, status, payment_id = '' }) {
88+
const [record] = await paymentsBase
89+
.select({
90+
filterByFormula: `{order_id} = "${razorpay_order_id}"`,
91+
view: Views.grid,
92+
})
93+
.firstPage();
94+
if (!record.id) {
95+
return;
96+
}
97+
return paymentsBase.update(record.id, { status, payment_id });
98+
}
99+
100+
78101
async function updateDonationBase({ razorpay_order_id, status, payment_id = '' }) {
79102
const [record] = await donationsBase
80103
.select({

src/pages/api/rzp.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Razorpay from 'razorpay';
22
import cuid from 'cuid';
33
import { NextApiRequest, NextApiResponse } from 'next';
44

5-
import { donationsBase, PaymentStatus, fundingsBase } from '../../services/airtable';
5+
import { donationsBase, PaymentStatus, fundingsBase, paymentsBase } from '../../services/airtable';
66
import { getFinalAmount } from '../../utils';
77

88
const RZP_KEY = process.env.NODE_ENV === 'development' ? process.env.RZP_TEST_KEY : process.env.RZP_LIVE_KEY;
@@ -18,7 +18,7 @@ const razorpay = new Razorpay(rzpCredentials);
1818

1919
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
2020
try {
21-
const { email, name, amount, phone, paymentMethod = 'Razorpay', campaign } = req.body;
21+
const { email, name, amount, phone, paymentMethod = 'Razorpay', campaign, isPayment } = req.body;
2222
const id = cuid();
2323
const data: { id: string; status: PaymentStatus } = await razorpay.orders.create({
2424
amount: getFinalAmount(Number(amount)) * 100, // in paise
@@ -30,6 +30,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
3030
phone,
3131
name,
3232
campaign,
33+
isPayment
3334
},
3435
});
3536
if (campaign) {
@@ -48,6 +49,21 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
4849
},
4950
{ typecast: true }
5051
);
52+
} else if(isPayment) {
53+
await paymentsBase.create(
54+
{
55+
id,
56+
name,
57+
email,
58+
phone,
59+
paid_amount: Number(amount),
60+
payment_method: paymentMethod,
61+
status: PaymentStatus.created,
62+
order_id: data.id,
63+
created_at: new Date().toISOString(),
64+
},
65+
{ typecast: true }
66+
);
5167
} else {
5268
await donationsBase.create(
5369
{

src/pages/pay.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import Link from 'next/link';
3+
import { BackIcon } from '../components/Icons/common';
4+
5+
import { SEO } from '../components/SEO';
6+
import { PaymentForm } from '../components/common/PaymentForm';
7+
8+
export default function DonatePage() {
9+
return (
10+
<>
11+
<SEO title="Pay to Coderplex Technologies" />
12+
<div className="pb-20 md:pb-0 md:max-w-3xl mx-auto">
13+
<header>
14+
<Link href="/">
15+
<a className="p-4 inline-block">
16+
<BackIcon />
17+
</a>
18+
</Link>
19+
</header>
20+
<PaymentForm isPayment={true} />
21+
</div>
22+
</>
23+
);
24+
}

src/services/airtable.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const airtableCredentials = {
44
key: process.env.AIRTABLE_KEY,
55
baseId: process.env.AIRTABLE_BASE_ID,
66
donationsTableName: process.env.AIRTABLE__DONATIONS_TABLE_NAME,
7+
paymentsTableName: process.env.AIRTABLE__PAYMENTS_TABLE_NAME,
78
campaignsTableName: process.env.AIRTABLE__CAMPAIGN_TABLE_NAME,
89
fundingsTableName: process.env.AIRTABLE__FUNDING_TABLE_NAME,
910
};
@@ -19,19 +20,26 @@ export enum PaymentStatus {
1920
failed = 'captured',
2021
}
2122

22-
export interface Donation {
23+
export interface RZPPayment {
2324
id: string;
2425
name: string;
2526
email: string;
2627
phone: string;
27-
donated_amount: number;
2828
payment_method: string;
2929
created_at: string;
3030
order_id: string;
3131
payment_id?: string;
3232
status: PaymentStatus;
3333
}
3434

35+
export interface Donation extends RZPPayment {
36+
donated_amount: number;
37+
}
38+
39+
export interface Payment extends RZPPayment {
40+
paid_amount: number;
41+
}
42+
3543
export interface Funding extends Donation {
3644
campaign: string;
3745
}
@@ -58,8 +66,10 @@ export interface CampaignWithFundings extends Campaign {
5866
export interface CampaignRow extends Airtable.FieldSet, Campaign {}
5967
export interface FundingRow extends Airtable.FieldSet, Funding {}
6068
export interface DonationRow extends Airtable.FieldSet, Donation {}
69+
export interface PaymentRow extends Airtable.FieldSet, Payment {}
6170

6271
const base = new Airtable({ apiKey: airtableCredentials.key }).base(airtableCredentials.baseId);
6372
export const donationsBase = base(airtableCredentials.donationsTableName) as Airtable.Table<DonationRow>;
73+
export const paymentsBase = base(airtableCredentials.paymentsTableName) as Airtable.Table<PaymentRow>;
6474
export const campaignsBase = base(airtableCredentials.campaignsTableName) as Airtable.Table<CampaignRow>;
6575
export const fundingsBase = base(airtableCredentials.fundingsTableName) as Airtable.Table<FundingRow>;

src/services/rzp.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ interface RzpData {
88
amount: number;
99
phone: string;
1010
campaign?: string;
11+
isPayment?: boolean;
1112
}
1213

1314
async function getOrderId(data: RzpData) {
@@ -48,7 +49,7 @@ export async function openRzp(data: RzpData) {
4849
image: CODERPLEX_LOGO,
4950
handler: async (res: RzpResponse) => {
5051
try {
51-
await updateStatus({ ...res, status: 'captured', campaign: data.campaign });
52+
await updateStatus({ ...res, status: 'captured', campaign: data.campaign, isPayment: data.isPayment });
5253
resolve();
5354
} catch (error) {
5455
reject(error);
@@ -60,6 +61,7 @@ export async function openRzp(data: RzpData) {
6061
razorpay_order_id: order.id,
6162
status: 'failed',
6263
campaign: data.campaign,
64+
isPayment: data.isPayment
6365
});
6466
reject(new Error(`Payment widget is closed without completing payment. Please try again!`));
6567
},

0 commit comments

Comments
 (0)