Skip to content

Commit

Permalink
feat(release): update 92ca053 (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
dtfiedler authored Jan 23, 2024
2 parents 1373088 + 34367fa commit 4c9d899
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 34 deletions.
11 changes: 6 additions & 5 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
NODE_ENV=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
PRIVATE_ROUTE_SECRET=
MIGRATE_ON_STARTUP=
NODE_ENV=test
PORT=4000
MIGRATE_ON_STARTUP=true
STRIPE_SECRET_KEY=test
STRIPE_WEBHOOK_SECRET=test
PRIVATE_ROUTE_SECRET=test
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ With a compatible system, follow these steps to start the upload service:
- `yarn start`
- alternatively use `yarn start:watch` to run the app in development mode with hot reloading provided by `nodemon`

Note: we store credentials for the service in AWS - to avoid these requests - set your NODE_ENV to `test` in your .env file.

## Database

The service relies on a postgres database. The following scripts can be used to create a local postgres database via docker:
Expand Down Expand Up @@ -62,16 +64,14 @@ Additional `knex` documentation can be found [here](https://knexjs.org/guide/mig

To run this service and a connected postgres database, fully migrated.

Run the container:

```shell
yarn start:docker
```
- `cp .env.sample .env` (and update values)
- `yarn start:docker` - run the local service and postgres database in docker containers

To build the image:
Alternatively, you can run the service in docker and connect to a local postgres database. You will need to standup `postgres` in a separate container.

```shell
docker build --build-arg NODE_VERSION=$(cat .nvmrc |cut -c2-8) --build-arg NODE_VERSION_SHORT=$(cat .nvmrc |cut -c2-3) .
```bash
docker run --name turbo-payment-service-postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres
docker run --env-file .env -p 4000:4000 ghcr.io/ardriveapp/turbo-payment-service:latest
```

## Tests
Expand Down
9 changes: 9 additions & 0 deletions docs/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,15 @@ paths:
schema:
"$ref": "#/components/schemas/PromoCode"

- name: uiMode
in: query
required: false
schema:
type: string
description: Which UI Mode to create the checkout session in
default: hosted
example: embedded

responses:
"200":
description: OK
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
"prom-client": "^14.1.0",
"raw-body": "^2.5.2",
"sinon-chai": "^3.7.0",
"stripe": "^11.13.0",
"stripe": "^14.11.0",
"validator": "^13.11.0",
"winston": "^3.8.2",
"yaml": "^2.2.2"
Expand Down
2 changes: 1 addition & 1 deletion src/middleware/verifySignature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export async function verifySignature(ctx: Context, next: Next): Promise<void> {

try {
if (!signature || !publicKey || !nonce) {
logger.info("Missing signature, public key or nonce");
logger.debug("Missing signature, public key or nonce");
return next();
}
logger.info("Verifying arweave signature");
Expand Down
46 changes: 36 additions & 10 deletions src/routes/topUp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { parseQueryParams } from "../utils/parseQueryParams";
import {
validateDestinationAddressType,
validateGiftMessage,
validateUiMode,
} from "../utils/validators";

export async function topUp(ctx: KoaContext, next: Next) {
Expand All @@ -51,6 +52,8 @@ export async function topUp(ctx: KoaContext, next: Next) {
address: rawDestinationAddress,
} = ctx.params;

const referer = ctx.headers.referer;

const loggerObject = { amount, currency, method, rawDestinationAddress };

if (!topUpMethods.includes(method)) {
Expand All @@ -63,6 +66,7 @@ export async function topUp(ctx: KoaContext, next: Next) {
const {
destinationAddressType: rawAddressType,
giftMessage: rawGiftMessage,
uiMode: rawUiMode,
} = ctx.query;

const destinationAddressType = rawAddressType
Expand All @@ -79,6 +83,11 @@ export async function topUp(ctx: KoaContext, next: Next) {
return next();
}

const uiMode = rawUiMode ? validateUiMode(ctx, rawUiMode) : "hosted";
if (uiMode === false) {
return next();
}

let destinationAddress: string;
if (destinationAddressType === "arweave") {
if (!isValidArweaveBase64URL(rawDestinationAddress)) {
Expand Down Expand Up @@ -207,6 +216,7 @@ export async function topUp(ctx: KoaContext, next: Next) {
{
...stripeMetadataRaw,
winstonCreditAmount: finalPrice.winc.toString(),
referer,
} as Record<string, string | number | null>
);

Expand All @@ -220,6 +230,7 @@ export async function topUp(ctx: KoaContext, next: Next) {
amount: actualPaymentAmount,
currency: payment.type,
metadata: stripeMetadata,
payment_method_types: ["card"],
});
} else {
const localGiftUrl = `http://localhost:5173`;
Expand All @@ -232,17 +243,31 @@ export async function topUp(ctx: KoaContext, next: Next) {
const urlEncodedGiftMessage = giftMessage
? encodeURIComponent(giftMessage)
: undefined;

const urls:
| { success_url: string; cancel_url: string }
| { redirect_on_completion: "never" } =
uiMode === "embedded"
? {
redirect_on_completion: "never",
}
: {
// // TODO: Success and Cancel URLS (Do we need app origin? e.g: ArDrive Widget, Top Up Page, ario-turbo-cli)
success_url: "https://app.ardrive.io",
cancel_url:
destinationAddressType === "email"
? `${giftUrl}?email=${destinationAddress}&amount=${
payment.amount
}${
urlEncodedGiftMessage
? `&giftMessage=${urlEncodedGiftMessage}`
: ""
}`
: "https://app.ardrive.io",
};

intentOrCheckout = await stripe.checkout.sessions.create({
// TODO: Success and Cancel URLS (Do we need app origin? e.g: ArDrive Widget, Top Up Page, ario-turbo-cli)
success_url: "https://app.ardrive.io",
cancel_url:
destinationAddressType === "email"
? `${giftUrl}?email=${destinationAddress}&amount=${payment.amount}${
urlEncodedGiftMessage
? `&giftMessage=${urlEncodedGiftMessage}`
: ""
}`
: "https://app.ardrive.io",
...urls,
currency: payment.type,
automatic_tax: {
enabled: !!process.env.ENABLE_AUTO_STRIPE_TAX || false,
Expand All @@ -269,6 +294,7 @@ export async function topUp(ctx: KoaContext, next: Next) {
metadata: stripeMetadata,
},
mode: "payment",
ui_mode: uiMode,
});
}
} catch (error) {
Expand Down
12 changes: 11 additions & 1 deletion src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ process.on("uncaughtException", (error) => {
logger.error("Uncaught exception:", error);
});

process.on("SIGTERM", () => {
logger.info("SIGTERM received, exiting...");
process.exit(0);
});

process.on("SIGINT", () => {
logger.info("SIGINT received, exiting...");
process.exit(0);
});

export async function createServer(
arch: Partial<Architecture>,
port: number = defaultPort
Expand Down Expand Up @@ -76,7 +86,7 @@ export async function createServer(
const paymentDatabase =
arch.paymentDatabase ?? new PostgresDatabase({ migrate: migrateOnStartup });
const stripe =
arch.stripe ?? new Stripe(STRIPE_SECRET_KEY, { apiVersion: "2022-11-15" });
arch.stripe ?? new Stripe(STRIPE_SECRET_KEY, { apiVersion: "2023-10-16" });

const emailProvider = (() => {
if (!isGiftingEnabled) {
Expand Down
24 changes: 24 additions & 0 deletions src/utils/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,27 @@ export function validateGiftMessage(

return validator.escape(message);
}

export const uiModes = ["hosted", "embedded"] as const;
export type UiMode = (typeof uiModes)[number];
function isUiMode(uiMode: string): uiMode is UiMode {
return uiModes.includes(uiMode as UiMode);
}
export function validateUiMode(
ctx: KoaContext,
uiMode: string | string[]
): UiMode | false {
const mode = validateSingularQueryParameter(ctx, uiMode);

if (!mode || !isUiMode(mode)) {
ctx.response.status = 400;
ctx.body = `Invalid ui mode! Allowed modes: "${uiModes.toString()}"`;
ctx.state.logger.error("Invalid ui mode!", {
query: ctx.query,
params: ctx.params,
});
return false;
}

return mode;
}
18 changes: 16 additions & 2 deletions tests/helpers/stubs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ export const paymentIntentStub = ({
statement_descriptor_suffix: null,
transfer_data: null,
transfer_group: null,
latest_charge: null,
payment_method_configuration_details: {
id: "pm_1MnkNVC8apPOWkDLH9wJvENb",
parent: "card_1MnkNVC8apPOWkDLH9wJvENb",
},
};
};

Expand All @@ -104,6 +109,8 @@ export const checkoutSessionStub = ({
custom_text: {
shipping_address: null,
submit: null,
after_submit: null,
terms_of_service_acceptance: null,
},
custom_fields: [],
customer_creation: null,
Expand Down Expand Up @@ -140,6 +147,13 @@ export const checkoutSessionStub = ({
success_url: "",
total_details: null,
url: null,
client_secret: null,
currency_conversion: null,
payment_method_configuration_details: {
id: "pm_1MnkNVC8apPOWkDLH9wJvENb",
parent: "card_1MnkNVC8apPOWkDLH9wJvENb",
},
ui_mode: "hosted",
};
};

Expand Down Expand Up @@ -233,7 +247,7 @@ export const stripeStubEvent = ({
}: {
eventObject: Stripe.Dispute | Stripe.PaymentIntent;
id?: string;
type: string;
type: "payment_intent.succeeded" | "charge.dispute.created";
}): Stripe.Event => {
return {
id,
Expand All @@ -247,7 +261,7 @@ export const stripeStubEvent = ({
type: type,
pending_webhooks: 0,
request: null,
};
} as Stripe.Event;
};

export const expectedArPrices = {
Expand Down
2 changes: 1 addition & 1 deletion tests/helpers/testHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export const coinGeckoOracle = new CoingeckoArweaveToFiatOracle(coinGeckoAxios);
export const arweaveToFiatOracle = new ReadThroughArweaveToFiatOracle({
oracle: coinGeckoOracle,
});
export const stripe = new Stripe("test", { apiVersion: "2022-11-15" });
export const stripe = new Stripe("test", { apiVersion: "2023-10-16" });
export const pricingService = new TurboPricingService({ arweaveToFiatOracle });
export const axios = axiosPackage.create({
baseURL: localTestUrl,
Expand Down
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5697,7 +5697,7 @@ __metadata:
rimraf: ^3.0.2
sinon: ^14.0.0
sinon-chai: ^3.7.0
stripe: ^11.13.0
stripe: ^14.11.0
ts-node: ^10.7.0
typescript: ^4.7.4
validator: ^13.11.0
Expand Down Expand Up @@ -6566,13 +6566,13 @@ __metadata:
languageName: node
linkType: hard

"stripe@npm:^11.13.0":
version: 11.18.0
resolution: "stripe@npm:11.18.0"
"stripe@npm:^14.11.0":
version: 14.11.0
resolution: "stripe@npm:14.11.0"
dependencies:
"@types/node": ">=8.1.0"
qs: ^6.11.0
checksum: 7da0195730df921c983560f22820797c1a9f8bc200e89012a87180d65628506a98fe400dca2c6e78ea98b9b931fac62dd9c223920566e0aa71468ec6cc671695
checksum: 3d4c683675c0046de567d286aa5c79e3b8e2551e9cb64921790c835fe1fbe1855a31332a014e74507fad793f3976f683e94d2126a9e30b137c13d4992d0818e7
languageName: node
linkType: hard

Expand Down

0 comments on commit 4c9d899

Please sign in to comment.