medusa-payment-tosspayments
Version:
TossPayments payment pluging for medusa
348 lines (347 loc) • 16.6 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const os_1 = require("os");
const medusa_1 = require("@medusajs/medusa");
const utils_1 = require("@medusajs/utils");
const tosspayments_1 = __importDefault(require("../core/tosspayments"));
class TossPaymentsProcessor extends medusa_1.AbstractPaymentProcessor {
constructor(container, options) {
super(container, options);
// you can access options here
// you can also initialize a client that
// communicates with a third-party service.
this.container_ = container;
this.options_ = {
key: options.tosspayments_key,
version: options.tosspayments_version,
debug: options.is_debug,
};
if (!options.tosspayments_key || !options.tosspayments_version) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_ARGUMENT, "Tosspayments processor need the tosspayments_key in options of medusa config");
}
/**
* Required TossPayments Options:
* - tosspayments_key(secret key): string
* - tosspayments_version: string;
*/
this.tosspayments_ = new tosspayments_1.default(this.options_.key, this.options_.version);
}
/**
* FRONTSTORE
* - initiatePayment
* - updatePayment
* - updatePaymentData
* - authorizePayment
*/
/**
* This method is called either if a region has only one payment provider enabled or when a Payment Session is selected,
* which occurs when the customer selects their preferred payment method during checkout.
* It is used to allow you to make any necessary calls to the third-party provider to initialize the payment.
* For example, in Stripe this method is used to create a Payment Intent for the customer.
*
* @param context
*/
async initiatePayment(context) {
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: initiatePayment", JSON.stringify(context, null, 2));
}
const { resource_id, email, amount } = context;
try {
const paymentIntent = this.tosspayments_.initialize({
id: resource_id,
email,
amount,
});
return {
session_data: paymentIntent,
};
}
catch (error) {
return this.buildError("Error in initiatePayment when initializing Tosspayments", error);
}
}
/**
* This method is used to update the payment session when the payment amount changes.
* It's called whenever the cart or any of its related data is updated.
* For example, when a line item is added to the cart or when a shipping method is selected.
*
* @param context
* @returns
*/
async updatePayment(context) {
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: updatePayment", JSON.stringify(context, null, 2));
}
const { resource_id, amount } = context;
const paymentIntent = this.tosspayments_.retrieve();
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: paymentIntent in updatePayment", JSON.stringify(paymentIntent, null, 2));
}
try {
if (!paymentIntent || paymentIntent.id !== resource_id) {
const reInitiatePayment = await this.initiatePayment(context);
if ((0, medusa_1.isPaymentProcessorError)(reInitiatePayment)) {
return this.buildError("Error in updatePayment during the re-initiate of the new payment for new customer", reInitiatePayment);
}
return reInitiatePayment;
}
if (!paymentIntent.email || paymentIntent.amount !== amount) {
const updatedPaymentIntent = this.tosspayments_.update({
email: context.email,
amount: context.amount,
});
return { session_data: updatedPaymentIntent };
}
const updatedPaymentIntent = this.tosspayments_.update(context.paymentSessionData);
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: updated paymentIntent in updatePayment", JSON.stringify(updatedPaymentIntent, null, 2));
}
return { session_data: updatedPaymentIntent };
}
catch (error) {
return this.buildError("Error in updatePayment during the retrieve of the cart", error);
}
}
/**
* This method is used to update the data field of a payment session.
* It's called when a request is sent to the Update Payment Session API Route, or when the CartService's updatePaymentSession is used.
* This method can also be used to update the data in the third-party payment provider, if necessary.
*
* @param sessionId
* @param data
* @returns
*/
async updatePaymentData(sessionId, data) {
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: updatePaymentData", JSON.stringify(data, null, 2));
}
try {
const paymentIntent = this.tosspayments_.retrieve();
if (data.amount !== paymentIntent.amount) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Can not update amount from updatePaymentData");
}
return {
...data,
};
}
catch (error) {
return this.buildError("Error in updatePaymentData during the retrieve of the custom data", error);
}
}
/**
* This method is used to get the status of a Payment or a Payment Session.
* Its main usage is within the place order and create swap flows.
* If the status returned is not authorized within these flows, then the payment is considered failed and an error will be thrown, stopping the flow from completion.
*
* @param paymentSessionData
* @returns
*/
async getPaymentStatus(paymentSessionData) {
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: getPaymentStatus", JSON.stringify(paymentSessionData, null, 2));
}
const id = paymentSessionData.id;
if (!id) {
return medusa_1.PaymentSessionStatus.PENDING;
}
const tosspaymentsStatus = this.tosspayments_.getStatus();
switch (tosspaymentsStatus) {
case "READY":
return medusa_1.PaymentSessionStatus.PENDING;
case "CANCELED":
case "PARTIAL_CANCELED":
return medusa_1.PaymentSessionStatus.CANCELED;
case "ABORTED":
case "EXPIRED":
return medusa_1.PaymentSessionStatus.ERROR;
case "IN_PROGRESS":
case "WAITING_FOR_DEPOSIT":
case "DONE":
return medusa_1.PaymentSessionStatus.AUTHORIZED;
default:
return medusa_1.PaymentSessionStatus.PENDING;
}
}
/**
* This method is used to authorize payment using the Payment Session of an order. This is called when the cart is completed and before the order is created.
* This method is also used for authorizing payments of a swap of an order and when authorizing sessions in a payment collection.
* You can interact with a third-party provider and perform any actions necessary to authorize the payment.
* The payment authorization might require additional action from the customer before it is declared authorized.
* Once that additional action is performed, the authorizePayment method will be called again to validate that the payment is now fully authorized.
* So, make sure to implement it for this case as well, if necessary.
* Once the payment is authorized successfully and the Payment Session status is set to authorized, the associated order or swap can then be placed or created.
* If the payment authorization fails, then an error will be thrown and the order will not be created.
*
* @param paymentSessionData
* @param context
* @returns
*/
async authorizePayment(paymentSessionData, context) {
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: authorizePayment", JSON.stringify({ sessionData: paymentSessionData, context }, null, 2));
}
try {
const { paymentKey, orderId, amount } = paymentSessionData;
if (!paymentKey || !orderId || !amount)
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, "Can not find necessary keys in authorizePayment");
const paymentData = {
paymentKey: paymentKey,
orderId: orderId,
amount: amount,
};
const paymentResult = (await this.tosspayments_.confirm(paymentData));
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: Confirm payment in authorizaePayment", JSON.stringify(paymentResult, null, 2));
}
const status = await this.getPaymentStatus(paymentSessionData);
return {
data: {
...paymentSessionData,
receipt: paymentResult.receipt.url,
checkout: paymentResult.checkout.url,
method: paymentResult.method,
card: paymentResult.card,
},
status,
};
}
catch (error) {
return this.buildError("Error in authorizePayment during the request to tosspayments api", error);
}
}
/**
* This method is used to provide a uniform way of retrieving the payment information from the third-party provider.
* For example, in Stripe’s Payment Processor this method is used to retrieve the payment intent details from Stripe.
*
* @param paymentSessionData
* @returns
*/
async retrievePayment(paymentSessionData) {
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: retrievePayment", JSON.stringify({ paymentSessionData }, null, 2));
}
const { paymentKey } = paymentSessionData;
try {
if (!paymentKey) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Can not find necessary keys in retrievePayment");
}
const paymentResult = (await this.tosspayments_.inquiry(paymentKey));
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: Inquiry payment detail in retrievePayment", JSON.stringify(paymentResult, null, 2));
}
return { ...paymentSessionData };
}
catch (error) {
return this.buildError("Error in retrievePayment getting payment information from tosspayments api", error);
}
}
/**
* ADMIN PANEL
* - refundPayment
* - cancelPayment
* - capturePayment
* - deletePayment
*/
/**
* This method is used to refund an order’s payment. This is typically triggered manually by the store operator from the admin.
* The refund amount might be the total order amount or part of it.
* This method is also used for refunding payments of a swap or a claim of an order, or when a request is sent to the Refund Payment API Route.
* You can utilize this method to interact with the third-party provider and perform any actions necessary to refund the payment.
*
* @param paymentSessionData
* @param refundAmount
* @returns
*/
async refundPayment(paymentSessionData, refundAmount) {
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: refundPayment", JSON.stringify({ paymentSessionData, refundAmount }, null, 2));
}
try {
const { paymentKey } = paymentSessionData;
if (!paymentKey) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_ARGUMENT, "Can not find necessary keys in refundPayment");
}
const paymentResult = (await this.tosspayments_.cancel(paymentKey, "Refund", refundAmount));
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: Refund payment in refundPayment", JSON.stringify(paymentResult, null, 2));
}
return paymentSessionData;
}
catch (error) {
return this.buildError("Error in refundPayment", error);
}
}
/**
* This method is used to cancel an order’s payment. This method is typically triggered by one of the following situations:
* 1. Before an order is placed and after the payment is authorized,
* an inventory check is done on products to ensure that products are still available for purchase.
* If the inventory check fails for any of the products, the payment is canceled.
* 2. If the store operator cancels the order from the admin.
* 3. When the payment of an order's swap is canceled.
* You can utilize this method to interact with the third-party provider and perform any actions necessary to cancel the payment.
*
* @param paymentSessionData
* @returns
*/
async cancelPayment(paymentSessionData) {
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: cancelPayment", JSON.stringify({ paymentSessionData }, null, 2));
}
try {
const { paymentKey } = paymentSessionData;
if (!paymentKey) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_ARGUMENT, "Can not find necessary keys in refundPayment");
}
const paymentResult = (await this.tosspayments_.cancel(paymentKey, "Cancel"));
if (this.options_.debug) {
console.info("TOSSPAYMENTS_PAYMENT_DEBUG: Refund payment in refundPayment", JSON.stringify(paymentResult, null, 2));
}
return paymentSessionData;
}
catch (error) {
return this.buildError("Error in refundPayment", error);
}
}
/**
* This method is used to capture the payment amount of an order. This is typically triggered manually by the store operator from the admin.
* This method is also used for capturing payments of a swap of an order, or when a request is sent to the Capture Payment API Route.
* You can utilize this method to interact with the third-party provider and perform any actions necessary to capture the payment.
*
* 토스페이먼츠에서는 결제 승인과 동시에 매입이 일어나므로 사용하지 않는다.
*
* @param paymentSessionData
*/
async capturePayment(paymentSessionData) {
return paymentSessionData;
}
/**
* This method is used to perform any actions necessary before a Payment Session is deleted. The Payment Session is deleted in one of the following cases:
* 1. When a request is sent to delete the Payment Session.
* 2. When the Payment Session is refreshed. The Payment Session is deleted so that a newer one is initialized instead.
* 3. When the Payment Processor is no longer available. This generally happens when the store operator removes it from the available Payment Processor in the admin.
* 4. When the region of the store is changed based on the cart information and the Payment Processor is not available in the new region.
*
* @param paymentSessionData
* @returns
*/
async deletePayment(paymentSessionData) {
return paymentSessionData;
}
buildError(message, e) {
var _a, _b;
return {
error: message,
code: "code" in e ? e.code : "",
detail: (0, medusa_1.isPaymentProcessorError)(e)
? `${e.error}${os_1.EOL}${(_a = e.detail) !== null && _a !== void 0 ? _a : ""}`
: "detail" in e
? e.detail
: (_b = e.message) !== null && _b !== void 0 ? _b : "",
};
}
}
TossPaymentsProcessor.identifier = "tosspayments";
exports.default = TossPaymentsProcessor;