Skip to content

Commit

Permalink
Merge branch 'develop' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
TedxTed committed Jan 22, 2025
2 parents 3dba953 + ac44f40 commit dca27e7
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 100 deletions.
77 changes: 27 additions & 50 deletions src/components/sale/OrderCollectionAdminCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Button, SkeletonText } from '@chakra-ui/react'
import { message, Table, Tooltip } from 'antd'
import { CardProps } from 'antd/lib/card'
import { ColumnProps } from 'antd/lib/table'
import axios from 'axios'
import PriceLabel from 'lodestar-app-element/src/components/labels/PriceLabel'
import ProductTypeLabel from 'lodestar-app-element/src/components/labels/ProductTypeLabel'
import TokenTypeLabel from 'lodestar-app-element/src/components/labels/TokenTypeLabel'
Expand All @@ -18,8 +17,13 @@ import { useIntl } from 'react-intl'
import { useHistory } from 'react-router-dom'
import styled from 'styled-components'
import hasura from '../../hasura'
import { dateFormatter, dateRangeFormatter, handleError } from '../../helpers'
import { codeMessages, commonMessages, saleMessages } from '../../helpers/translation'
import { dateFormatter, dateRangeFormatter } from '../../helpers'
import { commonMessages, saleMessages } from '../../helpers/translation'
import {
OpenPageMethod,
OrderPaymentStrategyContext,
PaymentMode,
} from '../../services/orderPayment/OrderPaymentStrategy'
import { OrderDiscountProps } from '../../types/checkout'
import { ShippingMethodType } from '../../types/merchandise'
import { ProductType } from '../../types/product'
Expand Down Expand Up @@ -270,54 +274,27 @@ const OrderCollectionAdminCard: React.VFC<
color: theme.colors.primary[500],
borderColor: theme.colors.primary[500],
}}
onClick={() => {
const mode = enabledModules.split_payment_mode ? 'split' : 'single'
switch (mode) {
case 'split':
axios
.get(`${process.env.REACT_APP_API_BASE_ROOT}/order/${record.id}/multi-payment/url`, {
params: {
appId,
},
headers: {
authorization: `Bearer ${authToken}`,
},
})
.then(
({
data: {
code,
result: { paymentUrl },
},
}) => {
if (code === 'SUCCESS') {
window.open(paymentUrl, '_blank')
} else {
message.error(formatMessage(codeMessages[code as keyof typeof codeMessages]))
}
},
)
.catch(handleError)
onClick={async () => {
const mode = enabledModules.split_payment_mode ? PaymentMode.Split : PaymentMode.Default
const result = await OrderPaymentStrategyContext.execute(mode, {
orderLogId: record.id,
appId,
authToken,
invoiceGatewayId: record.paymentLogs[0].invoiceGatewayId,
clientBackUrl: window.location.origin,
})

if (!result.success) {
message.error(result.message)
return
}

switch (result.openPageMethod) {
case OpenPageMethod.HISTORY_PUSH:
history.push(result.paymentUrl || '')
break
default:
axios
.post(
`${process.env.REACT_APP_API_BASE_ROOT}/tasks/payment/`,
{
orderId: record.id,
clientBackUrl: window.location.origin,
invoiceGatewayId: record.paymentLogs[0].invoiceGatewayId,
},
{ headers: { authorization: `Bearer ${authToken}` } },
)
.then(({ data: { code, result } }) => {
if (code === 'SUCCESS') {
history.push(`/tasks/payment/${result.id}`)
} else {
message.error(formatMessage(codeMessages[code as keyof typeof codeMessages]))
}
})
.catch(handleError)
case OpenPageMethod.OPEN_WINDOW:
window.location.replace(result.paymentUrl || '')
break
}
}}
Expand Down
73 changes: 23 additions & 50 deletions src/pages/OrderPage.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { gql, useQuery } from '@apollo/client'
import { Button, Icon, message, Typography } from 'antd'
import axios from 'axios'
import Tracking from 'lodestar-app-element/src/components/common/Tracking'
import { useApp } from 'lodestar-app-element/src/contexts/AppContext'
import { useAuth } from 'lodestar-app-element/src/contexts/AuthContext'
import { handleError, notEmpty } from 'lodestar-app-element/src/helpers'
import { notEmpty } from 'lodestar-app-element/src/helpers'
import { checkoutMessages } from 'lodestar-app-element/src/helpers/translation'
import { useResourceCollection } from 'lodestar-app-element/src/hooks/resource'
import { getResourceByProductId } from 'lodestar-app-element/src/hooks/util'
Expand All @@ -17,7 +16,8 @@ import AdminCard from '../components/common/AdminCard'
import PageHelmet from '../components/common/PageHelmet'
import DefaultLayout from '../components/layout/DefaultLayout'
import hasura from '../hasura'
import { codeMessages, commonMessages } from '../helpers/translation'
import { commonMessages } from '../helpers/translation'
import { OpenPageMethod, OrderPaymentStrategyContext, PaymentMode } from '../services/orderPayment/OrderPaymentStrategy'
import LoadingPage from './LoadingPage'
import NotFoundPage from './NotFoundPage'

Expand Down Expand Up @@ -264,54 +264,27 @@ const OrderPage: CustomVFC<{}, { order: hasura.PH_GET_ORDERS_PRODUCT['order_log_
</Link>
<Button
style={{ display: 'flex', alignItems: 'center', gap: 4 }}
onClick={param => {
const mode = enabledModules.split_payment_mode ? 'split' : 'single'
switch (mode) {
case 'split':
axios
.get(`${process.env.REACT_APP_API_BASE_ROOT}/order/${orderId}/multi-payment/url`, {
params: {
appId: appId,
},
headers: {
authorization: `Bearer ${authToken}`,
},
})
.then(
({
data: {
code,
result: { paymentUrl },
},
}) => {
if (code === 'SUCCESS') {
window.open(paymentUrl, '_blank')
} else {
message.error(formatMessage(codeMessages[code as keyof typeof codeMessages]))
}
},
)
.catch(handleError)
onClick={async param => {
const mode = enabledModules.split_payment_mode ? PaymentMode.Split : PaymentMode.Default
const result = await OrderPaymentStrategyContext.execute(mode, {
orderLogId: orderId,
appId,
authToken,
invoiceGatewayId: order.payment_logs[0].invoice_gateway_id,
clientBackUrl: window.location.origin,
})

if (!result.success) {
message.error(result.message)
return
}

switch (result.openPageMethod) {
case OpenPageMethod.HISTORY_PUSH:
history.push(result.paymentUrl || '')
break
default:
axios
.post(
`${process.env.REACT_APP_API_BASE_ROOT}/tasks/payment/`,
{
orderId: orderId,
clientBackUrl: window.location.origin,
invoiceGatewayId: order.payment_logs[0]?.invoice_gateway_id,
},
{ headers: { authorization: `Bearer ${authToken}` } },
)
.then(({ data: { code, result } }) => {
if (code === 'SUCCESS') {
history.push(`/tasks/payment/${result.id}`)
} else {
message.error(formatMessage(codeMessages[code as keyof typeof codeMessages]))
}
})
.catch(handleError)
case OpenPageMethod.OPEN_WINDOW:
window.location.replace(result.paymentUrl || '')
break
}
}}
Expand Down
159 changes: 159 additions & 0 deletions src/services/orderPayment/OrderPaymentStrategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import axios from 'axios'

type RecordType = {
orderLogId: string
appId: string
clientBackUrl: string
authToken?: string | null
invoiceGatewayId?: string | null
[key: string]: unknown
}

export enum PaymentMode {
Split = 'split',
Default = 'default',
}

export enum OpenPageMethod {
HISTORY_PUSH = 'history_push',
OPEN_WINDOW = 'open_window',
}

export type PaymentResult = {
success: boolean
message: string
openPageMethod?: OpenPageMethod
paymentUrl?: string
}

export interface PaymentStrategy {
execute(record: RecordType): Promise<PaymentResult>
}

type StandardApiResponse = {
code: string
message: string
openPageMethod?: OpenPageMethod
result: {
paymentUrl: string
[key: string]: any
}
}

abstract class BasePaymentStrategy implements PaymentStrategy {
async execute(record: RecordType): Promise<PaymentResult> {
try {
const standardResponse = await this.makeRequest(record)

if (standardResponse.code === 'SUCCESS') {
return {
success: true,
message: standardResponse.message,
paymentUrl: standardResponse.result.paymentUrl,
openPageMethod: standardResponse.openPageMethod,
}
} else {
const errorMessage = standardResponse.message
return {
success: false,
message: errorMessage,
}
}
} catch (error) {
const errorMessage = this.handleError(error)
return {
success: false,
message: errorMessage,
}
}
}

protected abstract makeRequest(record: RecordType): Promise<StandardApiResponse>

protected formatMessage(code: string): string {
const codeMessages: Record<string, string> = {
SUCCESS: 'Operation was successful',
ERROR: 'An error occurred',
}

return codeMessages[code as keyof typeof codeMessages] || 'Unknown error'
}

protected handleError(error: unknown): string {
console.error('Payment execution failed:', error)
return 'Failed to execute payment. Please try again later.'
}
}

export class OrderSplitPaymentStrategy extends BasePaymentStrategy {
protected async makeRequest(record: RecordType): Promise<StandardApiResponse> {
const response = await axios.post(
`${process.env.REACT_APP_API_BASE_ROOT}/order/${record.orderLogId}/payment/link`,
{ appId: record.appId },
)

const {
code,
message,
result: { link },
} = response.data

return {
code,
message,
openPageMethod: OpenPageMethod.OPEN_WINDOW,
result: {
paymentUrl: link,
},
}
}
}

export class OrderDefaultPaymentStrategy extends BasePaymentStrategy {
protected async makeRequest(record: RecordType): Promise<StandardApiResponse> {
const response = await axios.post(
`${process.env.REACT_APP_API_BASE_ROOT}/tasks/payment/`,
{
orderId: record.orderLogId,
clientBackUrl: record.clientBackUrl,
invoiceGatewayId: record.invoiceGatewayId,
},
{
headers: {
authorization: `Bearer ${record.authToken}`,
},
},
)

const { code, result, message } = response.data

return {
code,
message,
openPageMethod: OpenPageMethod.HISTORY_PUSH,
result: {
paymentUrl: `/tasks/payment/${result.id}`,
},
}
}
}

export class OrderPaymentStrategyContext {
static strategyMap = new Map<PaymentMode, new () => PaymentStrategy>([
[PaymentMode.Split, OrderSplitPaymentStrategy],
[PaymentMode.Default, OrderDefaultPaymentStrategy],
])

static execute(mode: PaymentMode, record: RecordType): Promise<PaymentResult> {
const strategy = this.getStrategy(mode)
return strategy.execute(record)
}

static getStrategy(mode: PaymentMode): PaymentStrategy {
const StrategyClass = this.strategyMap.get(mode) || this.strategyMap.get(PaymentMode.Default)
if (!StrategyClass) {
throw new Error('Default payment strategy not found')
}
return new StrategyClass()
}
}

0 comments on commit dca27e7

Please sign in to comment.