Skip to content

Commit

Permalink
Merge pull request #6 from Ahdeyyy/dedicated-virtual-account
Browse files Browse the repository at this point in the history
Dedicated virtual account
  • Loading branch information
Ahdeyyy authored Apr 18, 2024
2 parents a8c138d + 1b82e0b commit 4503b09
Show file tree
Hide file tree
Showing 13 changed files with 613 additions and 37 deletions.
5 changes: 5 additions & 0 deletions .changeset/clean-turkeys-wave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ahdeyy/paystack": patch
---

added dedicated virtual accounts endpoint
36 changes: 24 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ const archived_request = await paystack.payment_request.archive("request id")
#### Create customer

```typescript
let customer = await paystack.customer.create({
const customer = await paystack.customer.create({
email: "[email protected]",
first_name: "john",
last_name: "doe",
Expand All @@ -143,19 +143,19 @@ let customer = await paystack.customer.create({
#### List customers

```typescript
let customer = await paystack.customer.list({ perPage: 10, page: 1 })
const customer = await paystack.customer.list({ perPage: 10, page: 1 })
```

#### Fetch customer

```typescript
let customer = await paystack.customer.fetch(customers_email)
const customer = await paystack.customer.fetch(customers_email)
```

#### Validate customer

```typescript
let customer = await paystack.customer.validate(customer_code, {
const customer = await paystack.customer.validate(customer_code, {
country,
type,
account_number,
Expand All @@ -169,13 +169,13 @@ let customer = await paystack.customer.validate(customer_code, {
#### Update customer

```typescript
let customer = await paystack.customer.update(customer_code, { phone })
const customer = await paystack.customer.update(customer_code, { phone })
```

#### Whitelist/Blacklist customer

```typescript
let customer = await paystack.customer.whitelist_blacklist({
const customer = await paystack.customer.whitelist_blacklist({
customer: customer_code,
risk_action: "deny",
})
Expand All @@ -184,7 +184,7 @@ let customer = await paystack.customer.whitelist_blacklist({
#### Deactivate Authorization

```typescript
let customer = await paystack.customer.deactivate_authorization(
const customer = await paystack.customer.deactivate_authorization(
Authorization_code
)
```
Expand All @@ -194,7 +194,7 @@ let customer = await paystack.customer.deactivate_authorization(
#### Create product

```typescript
let product = await paystack.product.create({
const product = await paystack.product.create({
name: "sakura",
description: "cherry blossom",
price: 10000,
Expand All @@ -205,19 +205,19 @@ let product = await paystack.product.create({
#### List products

```typescript
let product = await paystack.product.list({ perPage: 10, page: 1 })
const product = await paystack.product.list({ perPage: 10, page: 1 })
```

#### Fetch product

```typescript
let product = await paystack.product.fetch(product_id)
const product = await paystack.product.fetch(product_id)
```

#### Update product

```typescript
let product = await paystack.product.update(product_id, { price: 69420 })
const product = await paystack.product.update(product_id, { price: 69420 })
```

## ROADMAP
Expand Down Expand Up @@ -254,10 +254,21 @@ let product = await paystack.product.update(product_id, { price: 69420 })
- [x] Update Product
- [x] Tests

- [ ] Dedicated Virtual Accounts
- [x] Create Dedicated Virtual Account
- [x] Assign Dedicated Virtual Account
- [x] List Dedicated Accounts
- [x] Fetch Dedicated Account
- [x] Requery Dedicated Account
- [x] Deactivate Dedicated Account
- [x] Split Dedicated Account Transaction
- [x] Remove Split from Dedicated Account
- [x] Fetch Bank Providers
- [ ] Tests

- [ ] Transactions
- [ ] Transaction Splits
- [ ] Terminal
- [ ] Dedicated Virtual Accounts
- [ ] Apple Pay
- [ ] Subaccounts
- [ ] Plans
Expand All @@ -273,6 +284,7 @@ let product = await paystack.product.update(product_id, { price: 69420 })
- [ ] Disputes
- [ ] Refunds
- [ ] Verification
- [ ] Miscellanous

## Testing

Expand Down
12 changes: 6 additions & 6 deletions lib/customer/customer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ export class Customer {
async list(query: ListCustomerData = { perPage: 50, page: 1 }): Promise<ListCustomerResponse> {
const header = this.get_headers();
const url = new URL(this.endpoint);

if (query.perPage) url.searchParams.set('perPage', query.perPage.toString())
if (query.page) url.searchParams.set('page', query.page.toString())

if (query.from) url.searchParams.set('from', query.from)
if (query.to) url.searchParams.set('to', query.to)
const keys = Object.keys(query)
for (let i = 0; i < keys.length; i++) {
const key = keys[i] ?? ''
const value = query[key]
url.searchParams.set(key, value)
}

const response = await fetch(url, {
headers: header,
Expand Down
3 changes: 2 additions & 1 deletion lib/customer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export type ListCustomerData = {
* @example 2016-09-24T00:00:05.000Z, 2016-09-21
* */
to?: string;
[key: string]: any;
}

export type Customer = {
Expand Down Expand Up @@ -280,4 +281,4 @@ export type WhitelistBlacklistCustomerResponse = { message: string; } & ({

export type DeactivateAuthorizationResponse = { message: string } & ({
status: true;
} | PaystackResponseError)
} | PaystackResponseError)
67 changes: 67 additions & 0 deletions lib/dedicated virtual accounts/dva.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { test, mock, expect } from "bun:test"
import { Paystack } from "../paystack"

// I can't test this suite properly because I don't have access to a nuban enabled business
// so this test would only check if the functions accurately pings the api endpoint with
// an expectation to fail

const paystack = mock(() => new Paystack(import.meta.env.PAYSTACK_SECRET_KEY ?? ""))

test("create DVA", async () => {
let response = await paystack().dva.create({ customer: import.meta.env.CUSTOMER_CODE ?? '' })
expect(response.status).toBe(false)
})

test("assign DVA", async () => {
let response = await paystack().dva.assign({
email: "[email protected]",
first_name: "Jane",
middle_name: "Karen",
last_name: "Doe",
phone: "+2348100000000",
preferred_bank: "test-bank",
country: "NG"

})
expect(response.status).toBe(false)
})
test("list DVA", async () => {
let response = await paystack().dva.list({ active: true, currency: "NGN" });
expect(response.status).toBe(false);
if (response.status) {
expect(response.data).toBeInstanceOf(Array)
expect(response.meta.perPage).toBe(10)
}
});

test("fetch DVA", async () => {
let response = await paystack().dva.fetch("foo")
expect(response.status).toBe(false)
})
test("requery DVA", async () => {
let response = await paystack().dva.requery({ account_number: "98897", provider_slug: "wema-bank" });
expect(response.status).toBe(false);
if (response.status) {
expect(response.message).toBeInstanceOf(String)
}
});

test("delete DVA", async () => {
let response = await paystack().dva.deactivate("foo")
expect(response.status).toBe(false)
})

test("split DVA", async () => {
let response = await paystack().dva.split({ customer: "janey" })
expect(response.status).toBe(false)
})

test("remove split DVA", async () => {
let response = await paystack().dva.remove_split("bar")
expect(response.status).toBe(false)

})
test("fetch bank providers DVA", async () => {
let response = await paystack().dva.fetch_bank_providers();
expect(response.status).toBe(false)
})
162 changes: 162 additions & 0 deletions lib/dedicated virtual accounts/dva.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import type { AssignDVAData, AssignDVAResponse, CreateDVAData, CreateDVAResponse, DeactivateDVAResponse, FetchBankProvidersDVAResponse, FetchDVAResponse, ListDVAQuery, ListDVAResponse, RemoveSplitDVAResponse, RequeryDVAQuery, RequeryDVAResponse, SplitDVAData, SplitDVAResponse } from "./types";

export class DedicatedVirtualAccounts {
private endpoint: string = "https://api.paystack.co/dedicated_account";

private secret_key: string;
constructor(secret_key: string) {
this.secret_key = secret_key;
}

/**
* Create a dedicated virtual account for an existing customer
* Bank Availability
* We currently support Wema Bank and Titan Paystack
* */
async create(data: CreateDVAData): Promise<CreateDVAResponse> {
const headers = this.get_headers();

const response = await fetch(this.endpoint, {
headers: headers,
method: "POST",
body: JSON.stringify(data)
})

const response_data = await response.json() as CreateDVAResponse

return response_data
}

/** With this endpoint, you can create a customer, validate the customer, and assign a DVA to the customer.
* Bank Availability
* We currently support Wema Bank and Titan Paystack.
* */
async assign(data: AssignDVAData): Promise<AssignDVAResponse> {
const headers = this.get_headers();

const response = await fetch(this.endpoint + "/assign", {
headers: headers,
method: "POST",
body: JSON.stringify(data)
})

const response_data = await response.json() as AssignDVAResponse;

return response_data;
}

/** List dedicated virtual accounts available on your integration. */
async list(query: ListDVAQuery): Promise<ListDVAResponse> {
const keys = Object.keys(query)
const url = new URL(this.endpoint)
for (let i = 0; i < keys.length; i++) {
const key = keys[i] ?? ''
const value = query[key]
url.searchParams.set(key, value)
}
const response = await fetch(url, {
headers: this.get_headers(),
method: "GET",
})

const response_data = await response.json() as ListDVAResponse

if (response_data.status) {
response_data.meta.page = Number(response_data.meta.page)
response_data.meta.total = Number(response_data.meta.total)
response_data.meta.perPage = Number(response_data.meta.perPage)
response_data.meta.skipped = Number(response_data.meta.skipped)
response_data.meta.pageCount = Number(response_data.meta.pageCount)
}

return response_data
}

async fetch(dedicated_account_id: string): Promise<FetchDVAResponse> {
const response = await fetch(`${this.endpoint}/${dedicated_account_id}`, {
headers: this.get_headers(),
method: "GET"
})
const response_data = await response.json() as FetchDVAResponse
return response_data
}

async requery(query: RequeryDVAQuery): Promise<RequeryDVAResponse> {
const keys = Object.keys(query)
const url = new URL(this.endpoint)
for (let i = 0; i < keys.length; i++) {
const key = keys[i] ?? ''
if (key === "account_number") {

const value = query[key]
url.searchParams.set("requery", value)
continue
}
const value = query[key]
url.searchParams.set(key, value)
}

const response = await fetch(url, {
headers: this.get_headers(),
method: "GET"
})

const response_data = await response.json() as RequeryDVAResponse

return response_data

}

async deactivate(dedicated_account_id: string): Promise<DeactivateDVAResponse> {
const response = await fetch(`${this.endpoint}/${dedicated_account_id}`, {
headers: this.get_headers(),
method: "DELETE",

})
const response_data = await response.json() as DeactivateDVAResponse;
return response_data
}

async split(data: SplitDVAData): Promise<SplitDVAResponse> {
const response = await fetch(`${this.endpoint}/split`, {
headers: this.get_headers(),
method: "POST",
body: JSON.stringify(data)
})
const response_data = await response.json() as SplitDVAResponse
return response_data
}


/*
* If you've previously set up split payment for transactions on a dedicated virtual account,
* you can remove it with this endpoint
* @params account_number Dedicated virtual account number
* */
async remove_split(account_number: string): Promise<RemoveSplitDVAResponse> {

const response = await fetch(`${this.endpoint}/split`, {
headers: this.get_headers(),
method: "DELETE",
body: JSON.stringify({ account_number: account_number })
})
const response_data = await response.json() as RemoveSplitDVAResponse
return response_data
}

async fetch_bank_providers(): Promise<FetchBankProvidersDVAResponse> {
const response = await fetch(`${this.endpoint}/available_providers`, {
headers: this.get_headers(),
method: "GET",
})
const response_data = await response.json() as FetchBankProvidersDVAResponse
return response_data
}

private get_headers() {
return {
"Content-Type": "application/json",
"Authorization": "Bearer " + this.secret_key
}
}
}
Loading

0 comments on commit 4503b09

Please sign in to comment.