Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(release): update f3c1916 #2

Merged
merged 1 commit into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions docs/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -544,3 +544,68 @@ paths:
schema:
type: string
default: "Invalid currency"

/redeem:
get:
summary: Redeem credits gifted via email
description: Redeem credits gifted via email by providing the destination wallet address for the credits, the redemption ID, and recipient email address

parameters:
- name: destinationAddress
in: query
required: true
schema:
type: string
description: Destination wallet address

- name: id
in: query
required: true
schema:
type: string
description: ID for the redemption

- name: email
in: query
required: true
schema:
type: string
description: Recipient email address for the redemption

responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Payment receipt redeemed for 1000 winc!
userBalance:
type: string
example: 1000
userAddress:
type: string
example: abcdefghijklmnopqrxtuvwxyz123456789ABCDEFGH
userCreationDate:
type: string
example: 2023-05-17T21:46:38.404Z

"400":
description: Bad Request
content:
text/plain:
schema:
type: string
description: "Error message string dependent on cause"
example: "Failure to redeem payment receipt!"

"503":
description: Service Unavailable
content:
text/plain:
schema:
type: string
default: "Error while redeeming payment receipt. Unable to reach Database!"
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@
"@types/koa": "^2.13.4",
"@types/koa-router": "^7.4.4",
"@types/koa__cors": "^3.3.0",
"@types/mandrill-api": "^1.0.33",
"@types/mocha": "^9.1.1",
"@types/node": "^18.16.1",
"@types/sinon": "^10.0.11",
"@types/sinon-chai": "^3.2.9",
"@types/validator": "^13.11.7",
"@typescript-eslint/eslint-plugin": "^5.25.0",
"@typescript-eslint/parser": "^5.25.0",
"axios-mock-adapter": "^1.21.2",
Expand Down Expand Up @@ -78,12 +80,14 @@
"koa-jwt": "^4.0.4",
"koa-router": "11.0.1",
"koa2-swagger-ui": "^5.8.0",
"mandrill-api": "^1.0.45",
"p-limit": "^3.1.0",
"pg": "^8.8.0",
"prom-client": "^14.1.0",
"raw-body": "^2.5.2",
"sinon-chai": "^3.7.0",
"stripe": "^11.13.0",
"validator": "^13.11.0",
"winston": "^3.8.2",
"yaml": "^2.2.2"
}
Expand Down
2 changes: 2 additions & 0 deletions src/architecture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
import Stripe from "stripe";

import { Database } from "./database/database";
import { EmailProvider } from "./emailProvider";
import { PricingService } from "./pricing/pricing";

export interface Architecture {
paymentDatabase: Database;
pricingService: PricingService;
stripe: Stripe;
emailProvider?: EmailProvider;
}
8 changes: 8 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,11 @@ export const recognizedCountries = [
export const promoCodeBackfills = {
welcomeTwentyPercentOff: "TOKEN2049",
};

export const maxGiftMessageLength = process.env.MAX_GIFT_MESSAGE_LENGTH ?? 250;

export const giftingEmailAddress =
process.env.GIFTING_EMAILL_ADDRESS ?? "[email protected]";

/** gifting on top up via email depends on GIFTING_ENABLED="true" env var */
export const isGiftingEnabled = process.env.GIFTING_ENABLED === "true";
8 changes: 7 additions & 1 deletion src/database/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
SingleUseCodePaymentCatalog,
TopUpQuote,
TopUpQuoteId,
UnredeemedGift,
UploadAdjustmentCatalog,
User,
UserAddress,
Expand All @@ -47,7 +48,7 @@ export interface Database {
getBalance: (userAddress: UserAddress) => Promise<WC>;
createPaymentReceipt: (
paymentReceipt: CreatePaymentReceiptParams
) => Promise<void>;
) => Promise<void | UnredeemedGift>;
getPaymentReceipt: (
paymentReceiptId: PaymentReceiptId
) => Promise<PaymentReceipt>;
Expand Down Expand Up @@ -77,4 +78,9 @@ export interface Database {
) => Promise<SingleUseCodePaymentCatalog[]>;
getUploadAdjustmentCatalogs: () => Promise<UploadAdjustmentCatalog[]>;
getPaymentAdjustmentCatalogs(): Promise<PaymentAdjustmentCatalog[]>;
redeemGift: (params: {
paymentReceiptId: string;
recipientEmail: string;
destinationAddress: string;
}) => Promise<User>;
}
14 changes: 14 additions & 0 deletions src/database/dbConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export const tableNames = {
paymentAdjustmentCatalog: "payment_adjustment_catalog",
singleUseCodePaymentAdjustmentCatalog:
"single_use_code_payment_adjustment_catalog",

unredeemedGift: "unredeemed_gift",
redeemedGift: "redeemed_gift",
} as const;

export const columnNames = {
Expand All @@ -52,6 +55,7 @@ export const columnNames = {
quoteExpirationDate: "quote_expiration_date",
quoteCreationDate: "quote_creation_date",
paymentProvider: "payment_provider",
giftMessage: "gift_message", // Optional gift message, ignored in non-gift top-ups for now

// Failed top up quote
failedReason: "failed_reason",
Expand Down Expand Up @@ -105,4 +109,14 @@ export const columnNames = {

operator: "operator",
operatorMagnitude: "operator_magnitude",

// Unredeemed Gift
giftedWincAmount: "gifted_winc_amount",
recipientEmail: "recipient_email",
senderEmail: "sender_email",
creationDate: "creation_date",
expirationDate: "expiration_date",

// Redeemed Gift
redemptionDate: "redemption_date",
} as const;
22 changes: 20 additions & 2 deletions src/database/dbMaps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
AdjustmentCatalogDBResult,
ChargebackReceipt,
ChargebackReceiptDBResult,
DestinationAddressType,
FailedTopUpQuote,
FailedTopUpQuoteDBResult,
PaymentAdjustmentCatalog,
Expand All @@ -31,9 +32,12 @@ import {
SingleUseCodePaymentCatalogDBResult,
TopUpQuote,
TopUpQuoteDBResult,
UnredeemedGift,
UnredeemedGiftDBResult,
UploadAdjustmentCatalog,
UploadAdjustmentCatalogDBResult,
User,
UserAddressType,
UserDBResult,
} from "./dbTypes";

Expand All @@ -47,7 +51,7 @@ export function userDBMap({
return {
promotionalInfo: promotional_info as PromotionalInfo,
userAddress: user_address,
userAddressType: user_address_type,
userAddressType: user_address_type as UserAddressType,
userCreationDate: user_creation_date,
winstonCreditBalance: new Winston(winston_credit_balance),
};
Expand All @@ -74,7 +78,7 @@ export function topUpQuoteDBMap({
quoteCreationDate: quote_creation_date,
quoteExpirationDate: quote_expiration_date,
destinationAddress: destination_address,
destinationAddressType: destination_address_type,
destinationAddressType: destination_address_type as DestinationAddressType,
winstonCreditAmount: new Winston(winston_credit_amount),
};
}
Expand Down Expand Up @@ -159,3 +163,17 @@ export function singleUseCodePaymentCatalogDBMap(
maximumDiscountAmount: dbResult.maximum_discount_amount,
};
}

export function unredeemedGiftDBMap(
dbResult: UnredeemedGiftDBResult
): UnredeemedGift {
return {
paymentReceiptId: dbResult.payment_receipt_id,
giftedWincAmount: new Winston(dbResult.gifted_winc_amount),
recipientEmail: dbResult.recipient_email,
giftMessage: dbResult.gift_message,
giftCreationDate: dbResult.creation_date,
giftExpirationDate: dbResult.expiration_date,
senderEmail: dbResult.sender_email,
};
}
53 changes: 50 additions & 3 deletions src/database/dbTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ export interface PaymentAdjustment extends Adjustment {
}

export type UserAddress = string | PublicArweaveAddress;
export type UserAddressType = string | "arweave";

export const userAddressTypes = ["arweave"] as const;
export type UserAddressType = (typeof userAddressTypes)[number];

export const destinationAddressTypes = ["email", "arweave"] as const;
export type DestinationAddressType = (typeof destinationAddressTypes)[number];

/** Currently using Postgres Date type (ISO String) */
export type Timestamp = string;
Expand Down Expand Up @@ -85,14 +90,15 @@ export interface User {
export interface TopUpQuote {
topUpQuoteId: TopUpQuoteId;
destinationAddress: UserAddress;
destinationAddressType: UserAddressType;
destinationAddressType: DestinationAddressType;
paymentAmount: PaymentAmount;
quotedPaymentAmount: PaymentAmount;
currencyType: CurrencyType;
winstonCreditAmount: WC;
quoteExpirationDate: Timestamp;
quoteCreationDate: Timestamp;
paymentProvider: PaymentProvider;
giftMessage?: string;
}

export type CreateTopUpQuoteParams = Omit<TopUpQuote, "quoteCreationDate"> & {
Expand All @@ -113,6 +119,7 @@ export interface CreatePaymentReceiptParams {
topUpQuoteId: TopUpQuoteId;
paymentAmount: PaymentAmount;
currencyType: CurrencyType;
senderEmail?: string;
}

export interface ChargebackReceipt extends PaymentReceipt {
Expand Down Expand Up @@ -180,7 +187,10 @@ export type AuditChangeReason =
| "payment"
| "account_creation"
| "chargeback"
| "refund";
| "refund"
| "gifted_payment"
| "gifted_payment_redemption"
| "gifted_account_creation";

export interface AuditLogInsert {
user_address: string;
Expand Down Expand Up @@ -209,6 +219,7 @@ export interface TopUpQuoteDBInsert {
winston_credit_amount: string;
payment_provider: string;
quote_expiration_date: string;
gift_message?: string;
}

export interface TopUpQuoteDBResult extends TopUpQuoteDBInsert {
Expand Down Expand Up @@ -338,3 +349,39 @@ export interface PaymentAdjustmentDBInsert extends AdjustmentDBInsert {
export interface PaymentAdjustmentDBResult
extends PaymentAdjustmentDBInsert,
AdjustmentDBResult {}

export interface UnredeemedGiftDBInsert {
payment_receipt_id: string;
gifted_winc_amount: string;
recipient_email: string;
sender_email?: string;
gift_message?: string;
}

export interface UnredeemedGiftDBResult extends UnredeemedGiftDBInsert {
creation_date: string;
expiration_date: string;
}

export interface RedeemedGiftDBInsert extends UnredeemedGiftDBResult {
destination_address: string;
}

export interface RedeemedGiftDBResult extends RedeemedGiftDBInsert {
redemption_date: string;
}

export interface UnredeemedGift {
paymentReceiptId: PaymentReceiptId;
giftedWincAmount: WC;
recipientEmail: string;
senderEmail?: string;
giftMessage?: string;
giftCreationDate: Timestamp;
giftExpirationDate: Timestamp;
}

export interface RedeemedGift extends UnredeemedGift {
destinationAddress: UserAddress;
redemptionDate: Timestamp;
}
14 changes: 14 additions & 0 deletions src/database/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,17 @@ export class PromoCodeExceedsMaxUses extends PromoCodeError {
this.name = "PromoCodeExceedsMaxUses";
}
}

export class GiftRedemptionError extends Error {
constructor(errorMessage = "Failure to redeem payment receipt!") {
super(errorMessage);
this.name = "GiftRedemptionError";
}
}

export class GiftAlreadyRedeemed extends GiftRedemptionError {
constructor() {
super("Gift has already been redeemed!");
this.name = "GiftAlreadyRedeemed";
}
}
Loading
Loading