Skip to content

Commit

Permalink
Recurring (#70)
Browse files Browse the repository at this point in the history
* Add recurring
  • Loading branch information
tomas-zijdemans-vipps authored Oct 4, 2024
1 parent 43cfa38 commit 12f66ba
Show file tree
Hide file tree
Showing 11 changed files with 4,446 additions and 4 deletions.
110 changes: 110 additions & 0 deletions sample_code/recurring_agreement_sample.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import "@std/dotenv/load";
import { Client } from "../src/mod.ts";

// First, get your API keys from https://portal.vipps.no/
// Here we assume they are stored in a .env file, see .env.example
const clientId = Deno.env.get("CLIENT_ID") || "";
const clientSecret = Deno.env.get("CLIENT_SECRET") || "";

const merchantSerialNumber = Deno.env.get("MERCHANT_SERIAL_NUMBER") || "";
const subscriptionKey = Deno.env.get("SUBSCRIPTION_KEY") || "";

const customerPhoneNumber = "4791234567";

// Create a client
const client = Client({
merchantSerialNumber,
subscriptionKey,
useTestMode: true,
retryRequests: false,
});

// Grab a token
const accessToken = await client.auth.getToken(clientId, clientSecret);

// Check if the token was retrieved successfully
if (!accessToken.ok) {
console.error("😟 Error retrieving token 😟");
console.error(accessToken.error);
Deno.exit(1);
}

const token = accessToken.data.access_token;

const agreement = await client.recurring.agreement.create(token, {
pricing: {
type: "LEGACY",
amount: 2500,
currency: "NOK",
},
interval: {
unit: "MONTH",
count: 1,
},
merchantRedirectUrl: "https://example.com/redirect",
merchantAgreementUrl: "https://example.com/agreement",
phoneNumber: customerPhoneNumber,
productName: "MyNews Digital",
});

// Check if the agreement was created successfully
if (!agreement.ok) {
console.error("😟 Error creating agreement 😟");
console.error(agreement.error);
Deno.exit(1);
}

const agreementInfo = await client.recurring.agreement.info(
token,
agreement.data.agreementId,
);

// Check if the agreement was retrieved successfully
if (!agreementInfo.ok) {
console.error("😟 Error retreiving agreement 😟");
console.error(agreementInfo.error);
Deno.exit(1);
}

const activatedAgreement = await client.recurring.agreement.forceAccept(
token,
agreement.data.agreementId,
{
phoneNumber: customerPhoneNumber,
},
);

// Check if the agreement was retrieved successfully
if (!activatedAgreement.ok) {
console.error("😟 Error force accepting the agreement 😟");
console.error(activatedAgreement.error);
Deno.exit(1);
}

const updatedAgreement = await client.recurring.agreement.update(
token,
agreement.data.agreementId,
{ status: "STOPPED" },
);

// Check if the agreement was retrieved successfully
if (!updatedAgreement.ok) {
console.error("😟 Error updating agreement 😟");
console.error(updatedAgreement.error);
Deno.exit(1);
}

const listAgreements = await client.recurring.agreement.list(
token,
"STOPPED",
1,
);

// Check if the agreements was retrieved successfully
if (!listAgreements.ok) {
console.error("😟 Error retreiving agreements 😟");
console.error(listAgreements.error);
Deno.exit(1);
}

console.log(listAgreements.data);
108 changes: 108 additions & 0 deletions sample_code/recurring_charge_sample.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import "@std/dotenv/load";
import { Client } from "../src/mod.ts";

// First, get your API keys from https://portal.vipps.no/
// Here we assume they are stored in a .env file, see .env.example
const clientId = Deno.env.get("CLIENT_ID") || "";
const clientSecret = Deno.env.get("CLIENT_SECRET") || "";

const merchantSerialNumber = Deno.env.get("MERCHANT_SERIAL_NUMBER") || "";
const subscriptionKey = Deno.env.get("SUBSCRIPTION_KEY") || "";

const customerPhoneNumber = "4791234567";

// Create a client
const client = Client({
merchantSerialNumber,
subscriptionKey,
useTestMode: true,
retryRequests: false,
});

// Grab a token
const accessToken = await client.auth.getToken(clientId, clientSecret);

// Check if the token was retrieved successfully
if (!accessToken.ok) {
console.error("😟 Error retrieving token 😟");
console.error(accessToken.error);
Deno.exit(1);
}

const token = accessToken.data.access_token;

const agreement = await client.recurring.agreement.create(token, {
pricing: {
type: "LEGACY",
amount: 2500,
currency: "NOK",
},
interval: {
unit: "MONTH",
count: 1,
},
merchantRedirectUrl: "https://example.com/redirect",
merchantAgreementUrl: "https://example.com/agreement",
phoneNumber: customerPhoneNumber,
productName: "MyNews Digital",
});

// Check if the agreement was created successfully
if (!agreement.ok) {
console.error("😟 Error creating agreement 😟");
console.error(agreement.error);
Deno.exit(1);
}

const agreementId = agreement.data.agreementId;

const acceptedAgreement = await client.recurring.agreement.forceAccept(
token,
agreementId,
{ phoneNumber: customerPhoneNumber },
);

// Check if the agreement was accepted successfully
if (!acceptedAgreement.ok) {
console.error("😟 Error accepting agreement 😟");
console.error(acceptedAgreement.error);
Deno.exit(1);
}

// 10 days from now in YYYY-MM-DD format
const tenDaysFromToday = new Date(Date.now() + 10 * 24 * 60 * 60 * 1000)
.toISOString().split("T")[0];

const charge = await client.recurring.charge.create(token, agreementId, {
amount: 2500,
description: "MyNews Digital",
orderId: crypto.randomUUID(),
due: tenDaysFromToday,
retryDays: 5,
transactionType: "DIRECT_CAPTURE",
type: "RECURRING",
});

// Check if the charge was created successfully
if (!charge.ok) {
console.error("😟 Error creating charge 😟");
console.error(charge.error);
Deno.exit(1);
}

const chargeId = charge.data.chargeId || "";

const chargeInfo = await client.recurring.charge.info(
token,
agreementId,
chargeId,
);

// Check if the charge info was fetched successfully
if (!chargeInfo.ok) {
console.error("😟 Error retrieving charge 😟");
console.error(chargeInfo.error);
Deno.exit(1);
}

console.log(chargeInfo.data);
17 changes: 13 additions & 4 deletions scripts/generate_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ await run(`rm -rf src/generated_types/checkout/index.ts`);

// ePayment API
await createClient({
input:
"https://developer.vippsmobilepay.com/redocusaurus/epayment-swagger-id.yaml",
input: "https://developer.vippsmobilepay.com/redocusaurus/epayment-swagger-id.yaml",
output: "src/generated_types/epayment",
services: false,
exportCore: false,
Expand All @@ -37,10 +36,20 @@ await createClient({

await run(`rm -rf src/generated_types/epayment/index.ts`);

// Recurring API
await createClient({
input: "https://developer.vippsmobilepay.com/redocusaurus/recurring-swagger-id.yaml",
output: "src/generated_types/recurring",
services: false,
exportCore: false,
schemas: false,
});

await run(`rm -rf src/generated_types/recurring/index.ts`);

// Webhooks API
await createClient({
input:
"https://developer.vippsmobilepay.com/redocusaurus/webhooks-swagger-id.yaml",
input: "https://developer.vippsmobilepay.com/redocusaurus/webhooks-swagger-id.yaml",
output: "src/generated_types/webhooks",
services: false,
exportCore: false,
Expand Down
14 changes: 14 additions & 0 deletions src/api_proxy.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import { authRequestFactory } from "./apis/auth.ts";
import { checkoutRequestFactory } from "./apis/checkout.ts";
import { ePaymentRequestFactory } from "./apis/epayment.ts";
import {
agreementRequestFactory,
chargeRequestFactory,
} from "./apis/recurring.ts";
import { webhooksRequestFactory } from "./apis/webhooks.ts";
import type { ApiProxy, BaseClient, RequestFactory } from "./types_internal.ts";

export type SDKClient = {
auth: ReturnType<typeof proxifyFactory<typeof authRequestFactory>>;
checkout: ReturnType<typeof proxifyFactory<typeof checkoutRequestFactory>>;
payment: ReturnType<typeof proxifyFactory<typeof ePaymentRequestFactory>>;
recurring: {
agreement: ReturnType<
typeof proxifyFactory<typeof agreementRequestFactory>
>;
charge: ReturnType<typeof proxifyFactory<typeof chargeRequestFactory>>;
};
webhook: ReturnType<typeof proxifyFactory<typeof webhooksRequestFactory>>;
};

Expand All @@ -21,6 +31,10 @@ export const proxifyClient = (client: BaseClient): SDKClient => {
auth: proxifyFactory(client, authRequestFactory),
checkout: proxifyFactory(client, checkoutRequestFactory),
payment: proxifyFactory(client, ePaymentRequestFactory),
recurring: {
charge: proxifyFactory(client, chargeRequestFactory),
agreement: proxifyFactory(client, agreementRequestFactory),
},
webhook: proxifyFactory(client, webhooksRequestFactory),
} as const;
};
Expand Down
Loading

0 comments on commit 12f66ba

Please sign in to comment.