Skip to content

Commit

Permalink
Merge pull request #22 from a11rew/ag/paystack-sdk-update
Browse files Browse the repository at this point in the history
  • Loading branch information
a11rew authored Aug 20, 2023
2 parents 1430481 + 9b2a885 commit 2184c7f
Show file tree
Hide file tree
Showing 10 changed files with 347 additions and 413 deletions.
7 changes: 7 additions & 0 deletions .changeset/curly-bulldogs-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"medusa-payment-paystack": patch
---

Removes outdated Paystack API wrapper package we were using prior fixing deprecated dependency warnings.

Also changes how we generate references. Transactions are initialized with Paystack and the returned reference used instead of an arbitrary cuid.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

- [Paystack account](https://dashboard.paystack.com/#/signup)
- [Paystack account's secret key](https://support.paystack.com/hc/en-us/articles/360009881600-Paystack-Test-Keys-Live-Keys-and-Webhooks)
- Medusa server running at least `@medusajs/medusa^1.5.0`
- Medusa server

## Medusa Server

Expand Down Expand Up @@ -67,15 +67,13 @@ Using this returned reference as the Paystack transaction's reference allows the

### Using Transaction Reference

`medusa-payment-paystack` inserts a `paystackTxRef` into the [`PaymentSession`](https://docs.medusajs.com/advanced/backend/payment/overview/#payment-session)'s data.
`medusa-payment-paystack` inserts a reference named `paystackTxRef` into the [`PaymentSession`](https://docs.medusajs.com/advanced/backend/payment/overview/#payment-session)'s data.

```
const { paystackTxRef } = paymentSession.data
```

Provide this reference when initiating any of the Paystack [Accept Payment](https://paystack.com/docs/payments/accept-payments/) flows.

For example, when using the [Paystack Popup](https://paystack.com/docs/payments/accept-payments/#popup), provide this reference to the initialization method;
Provide this reference when initiating the Paystack [Popup](https://paystack.com/docs/payments/accept-payments/#popup) payment flow.

```js
const paymentForm = document.getElementById('paymentForm');
Expand Down
4 changes: 1 addition & 3 deletions packages/plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
"@medusajs/medusa": "^1.13.0"
},
"dependencies": {
"@medusajs/utils": "^1.9.4",
"@paralleldrive/cuid2": "^2.2.1",
"paystack-api": "^2.0.6"
"@medusajs/utils": "^1.9.4"
},
"devDependencies": {
"@medusajs/medusa": "^1.13.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ export const PaystackProviderServiceMock = {
});
case "123-false":
return Promise.resolve({
status: false,
data: {
status: false,
status: "failed",
id: "123",
},
});
Expand All @@ -37,6 +38,15 @@ export const PaystackProviderServiceMock = {
}
}),

initialize: jest.fn().mockImplementation(({ amount, email }) => {
return Promise.resolve({
data: {
reference: "ref-" + Math.random() * 1000,
authorization_url: "https://paystack.com/123",
},
});
}),

get: jest.fn().mockImplementation(({ id }) => {
switch (id) {
case "123-success":
Expand All @@ -50,8 +60,9 @@ export const PaystackProviderServiceMock = {

case "123-false":
return Promise.resolve({
status: false,
data: {
status: false,
status: "failed",
paystackTxId: id,
paystackTxData: {},
},
Expand Down Expand Up @@ -80,6 +91,4 @@ export const PaystackProviderServiceMock = {
},
};

const paystackapi = jest.fn(() => PaystackProviderServiceMock);

export default paystackapi;
export default jest.fn(() => PaystackProviderServiceMock);
163 changes: 163 additions & 0 deletions packages/plugin/src/lib/paystack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import https from "https";

import { SupportedCurrency } from "../utils/currencyCode";

const PAYSTACK_API_PATH = "https://api.paystack.co";

type HTTPMethod =
| "GET"
| "POST"
| "PUT"
| "PATCH"
| "DELETE"
| "OPTIONS"
| "HEAD";

type PaystackResponse<T> = {
status: boolean;
message: string;
data: T;
};

interface Request {
path: string;
method: HTTPMethod;
headers?: Record<string, string>;
body?: Record<string, unknown>;
query?: Record<string, string>;
}

export interface PaystackTransactionAuthorisation {
reference: string;
authorization_url: string;
access_code: string;
}

export default class Paystack {
apiKey: string;

constructor(apiKey: string) {
this.apiKey = apiKey;
}

protected async requestPaystackAPI<T>(request: Request): Promise<T> {
const path =
request.path.replace(/\/$/, "") +
"/?" +
new URLSearchParams(request.query).toString();

const options = {
method: request.method,
path,
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
},
};

return new Promise((resolve, reject) => {
const req = https.request(PAYSTACK_API_PATH, options, res => {
let data: Uint8Array[] = [];

res.on("data", chunk => {
data.push(chunk);
});

res.on("end", () => {
try {
resolve(JSON.parse(Buffer.concat(data).toString()) as T);
} catch (e) {
reject(e);
}
});
});

req.on("error", e => {
reject(e);
});

if (request.body && Object.values(request.body).length > 0) {
req.write(JSON.stringify(request.body));
}

req.end();
});
}

transaction = {
verify: ({ reference }: { reference: string }) =>
this.requestPaystackAPI<
PaystackResponse<{
id: number;
status: string;
reference: string;
}>
>({
path: "/transaction/verify/" + reference,
method: "GET",
}),
get: ({ id }: { id: string }) =>
this.requestPaystackAPI<
PaystackResponse<{
id: number;
status: string;
reference: string;
}>
>({
path: "/transaction/" + id,
method: "GET",
}),
initialize: ({
amount,
email,
currency,
reference,
}: {
amount: number;
email?: string;
currency?: SupportedCurrency;
reference?: string;
}) =>
this.requestPaystackAPI<
PaystackResponse<{
authorization_url: string;
access_code: string;
reference: string;
}>
>({
path: "/transaction/initialize",
method: "POST",
body: {
amount,
email,
currency,
reference,
},
}),
};

refund = {
create: ({
transaction,
amount,
}: {
transaction: string;
amount: number;
}) =>
this.requestPaystackAPI<
PaystackResponse<{
id: number;
status: string;
reference: string;
amount: number;
}>
>({
path: "/refund",
method: "POST",
body: {
transaction,
amount,
},
}),
};
}
Loading

0 comments on commit 2184c7f

Please sign in to comment.