You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have created a custom payment provider for Mollie using the steps mentioned in the docs. I am able to initiate and complete the payment, but when the payment is complete and the webhook event getWebhookActionAndData is triggered, I am getting the following error. Due to this, I am unable to place an order.
error: An error occurred while processing payment.webhook_received
error: payment - id must be defined
{
message: 'payment - id must be defined',
name: 'Error',
stack: 'Error: payment - id must be defined\n' +
' at AbstractService_.retrieve (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/utils/src/modules-sdk/medusa-internal-service.ts:122:15)\n' +
' at AbstractService_.descriptor.value (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/utils/src/modules-sdk/decorators/inject-manager.ts:54:29)\n' +
' at PaymentModuleService.capturePayment_ (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/payment/src/services/payment-module.ts:633:24)\n' +
' at transaction.transaction (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/utils/src/modules-sdk/decorators/inject-transaction-manager.ts:63:39)\n' +
' at fork.getConnection.transactional (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@mikro-orm/core/EntityManager.js:774:35)\n' +
' at PostgreSqlConnection.transactional (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@mikro-orm/knex/AbstractSqlConnection.js:36:31)\n' +
' at processTicksAndRejections (node:internal/process/task_queues:95:5)\n' +
' at async transactionWrapper (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/utils/src/dal/utils.ts:32:10)\n' +
' at async MikroOrmBaseRepository.transaction (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/utils/src/dal/mikro-orm/mikro-orm-repository.ts:74:12)\n' +
' at async PaymentModuleService.descriptor.value (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/utils/src/modules-sdk/decorators/inject-transaction-manager.ts:32:14)\n' +
'⮑ sat /home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/core-flows/dist/payment/workflows/capture-payment.js: [capture-payment-workflow -> capture-payment-step (invoke)]\n' +
'⮑ sat /home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/core-flows/dist/payment/workflows/process-payment.js: [process-payment-workflow -> capture-payment-workflow-as-step (invoke)]',
__isMedusaError: true,
type: 'not_found',
code: undefined,
date: 2024-12-04T23:20:47.751Z
}
Logs from admin dash workflow
Error Logs
{
"id": "wf_exec_01JE9ZKPV6QNRJBMVBR9RJRNE3",
"workflow_id": "complete-cart",
"errors": [
{
"error": {
"name": "Error",
"stack": "Error: You must pass a non-undefined value to the property currency_code of entity Cart.\n at Function.assignProperty (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@mikro-orm/core/entity/EntityAssigner.js:46:19)\n at /home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@mikro-orm/core/entity/EntityAssigner.js:31:35\n at Array.forEach (<anonymous>)\n at Function.assign (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@mikro-orm/core/entity/EntityAssigner.js:30:27)\n at SqlEntityManager.assign (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@mikro-orm/core/EntityManager.js:995:40)\n at /home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/utils/src/dal/mikro-orm/mikro-orm-repository.ts:437:17\n at Array.map (<anonymous>)\n at MikroOrmAbstractBaseRepository_.update (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/utils/src/dal/mikro-orm/mikro-orm-repository.ts:436:12)\n at processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async AbstractService_.update (/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/utils/src/modules-sdk/medusa-internal-service.ts:348:14)\n⮑ sat /home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/core-flows/dist/cart/workflows/complete-cart.js: [complete-cart -> update-carts (invoke)]",
"message": "You must pass a non-undefined value to the property currency_code of entity Cart."
},
"action": "update-carts",
"handlerType": "compensate"
}
],
"execution": {
"state": "failed",
"modelId": "complete-cart",
"options": {
"name": "complete-cart",
"store": true,
"idempotent": true,
"retentionTime": 259200,
"storeExecution": true
},
"metadata": {
"sourcePath": "/home/al-sirang/Desktop/office-projects/medusa/server/node_modules/@medusajs/core-flows/dist/cart/workflows/complete-cart.js",
"eventGroupId": "01JE9ZKPTNWTZT8KPY1A92FKWP",
"parentStepIdempotencyKey": "process-payment-workflow:01JE9ZKPTNAC5HZD5A48MHJNG3:complete-cart-as-step:invoke"
}
},
"state": "failed"
}
MolliePaymentProviderService implementation
typeMollieOptions={apiKey: string;profileId?: string;testMode?: boolean;webhookUrl?: string;redirectUrl?: string;};classMolliePaymentProviderServiceextendsAbstractPaymentProvider<MollieOptions>{staticidentifier="mollie";protectedoptions_: MollieOptions;privatemollieClient: ReturnType<typeofMollieClient>;staticvalidateOptions(options: Record<any,any>){if(!options.apiKey){thrownewMedusaError(MedusaError.Types.INVALID_DATA,"API key is required in the provider's options.");}}constructor(container: Record<string,any>&MedusaContainer,options: MollieOptions){super(container,options);this.options_=options;this.mollieClient=MollieClient({apiKey: options.apiKey,});}asyncinitiatePayment(context: CreatePaymentProviderSession): Promise<PaymentProviderError|PaymentProviderSessionResponse>{try{const{ currency_code, amount }=context;constformattedAmount=(amountasnumber).toFixed(2);constpayment=awaitthis.mollieClient.payments.create({method: PaymentMethod.creditcard,/// test payment method to creditCard (for testing)amount: {currency: currency_code.toUpperCase(),value: formattedAmount,},description: "Payment for order",redirectUrl: `${process.env.MOLLIE_REDIRECT_URL}`,webhookUrl: `${this.options_.webhookUrl}/hooks/payment/mollie_mollie`,// Only add customer details if they exist
...(context.context?.email&&{billingEmail: context.context.email,}),metadata: {// Add metadata only if customer exists
...(context.context?.customer?.id&&{customerId: context.context.customer.id,has_account: context.context.customer.has_account,}),session_id: context.context.session_id,},});constinitiatePayment: Record<string,unknown>=paymentasany;return{data: initiatePayment,};}catch(error){returnthis.handleError(error);}}asyncgetPaymentStatus(paymentSessionData: Record<string,unknown>): Promise<PaymentSessionStatus>{constpayment=awaitthis.mollieClient.payments.get(paymentSessionData.idasstring);returnthis.mapPaymentStatus(payment.status);}asyncretrievePayment(paymentSessionData: Record<string,unknown>): Promise<Record<string,unknown>|PaymentProviderError>{try{constpayment=awaitthis.mollieClient.payments.get(paymentSessionData.idasstring);return{id: payment.id,status: payment.status,};}catch(error){returnthis.handleError(error);}}asyncauthorizePayment(paymentSessionData: Record<string,unknown>): Promise<|PaymentProviderError|{status: PaymentSessionStatus;data: PaymentProviderSessionResponse["data"];}>{try{constexternalId=paymentSessionData.idasstring;constpayment=awaitthis.mollieClient.payments.get(externalId);return{status: this.mapPaymentStatus(payment.status),data: {
...payment,session_id: (payment.metadataasRecord<string,any>).session_id,id: externalId,amount: payment.amount.value,currency_code: payment.amount.currency,},};}catch(error){returnthis.handleError(error);}}/// todo: change this method to an ObjectprivatemapPaymentStatus(status: string): PaymentSessionStatus{switch(status){case"paid":
returnPaymentSessionStatus.CAPTURED;case"authorized":
returnPaymentSessionStatus.AUTHORIZED;case"canceled":
returnPaymentSessionStatus.CANCELED;case"pending":
returnPaymentSessionStatus.PENDING;case"expired":
returnPaymentSessionStatus.ERROR;default:
returnPaymentSessionStatus.PENDING;}}privatehandleError(error: any): PaymentProviderError{console.error("Mollie Payment Provider Error:",error);return{error: error,code: error.response?.status||"unknown",detail: error?.response?.data?.detail||error.message,};}asyncupdatePayment(context: UpdatePaymentProviderSession): Promise<PaymentProviderError|PaymentProviderSessionResponse>{try{/// this method is not invokedconst{ amount, currency_code, data,context: customerDetails}=context;constexternalId=data.id;/// todo: handle update logicconstpayment=awaitthis.mollieClient.payments.update(externalIdasstring,{description: "",});return{data: {id: payment.id,status: payment.status,},};}catch(error){returnthis.handleError(error);}}asyncdeletePayment(paymentSessionData: Record<string,unknown>): Promise<PaymentProviderError|Record<string,unknown>>{returnthis.cancelPayment(paymentSessionData);}asynccapturePayment(paymentData: Record<string,unknown>): Promise<PaymentProviderError|Record<string,unknown>>{try{constexternalId=paymentData.idasstring;constpayment=awaitthis.mollieClient.payments.get(externalId);return{
...payment,id: externalId,};}catch(error){returnthis.handleError(error);}}asyncrefundPayment(paymentData: Record<string,unknown>,refundAmount: number): Promise<PaymentProviderError|Record<string,unknown>>{try{constrefund=awaitthis.mollieClient.paymentRefunds.create({paymentId: paymentData.idasstring,amount: {currency: paymentData.currency_codeasstring,value: `${refundAmount.toFixed(2)}`,},});return{id: refund.id,status: refund.status,};}catch(error){returnthis.handleError(error);}}asynccancelPayment(paymentData: Record<string,unknown>): Promise<PaymentProviderError|Record<string,unknown>>{try{// Use the stored paymentId from the session dataconstpaymentId=paymentData.idasstring;if(!paymentId){thrownewError("No payment ID found");}constpayment=awaitthis.mollieClient.payments.get(paymentId);return{id: payment.id,status: "canceled",};}catch(error){returnthis.handleError(error);}}asyncgetWebhookActionAndData(payload: ProviderWebhookPayload["payload"]): Promise<WebhookActionResult>{try{const{ data }=payload;constpayment=awaitthis.mollieClient.payments.get(data.idasstring);switch(payment.status){case"paid":
return{action: "captured",data: {session_id: (payment.metadataasRecord<string,any>).session_id,amount: newBigNumber(payment.amount.value),},};case"authorized":
return{action: "authorized",data: {session_id: (data.metadataasRecord<string,any>).session_id,amount: newBigNumber(payment.amount.value),},};default:
return{action: "not_supported"};}}catch(error){return{action: "failed"};}}}exportdefaultMolliePaymentProviderService;
If you need any additional data, please let me know. Any kind of help will be appreciated. Thank you!
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Hello everyone,
I have created a custom payment provider for Mollie using the steps mentioned in the docs. I am able to initiate and complete the payment, but when the payment is complete and the webhook event
getWebhookActionAndData
is triggered, I am getting the following error. Due to this, I am unable to place an order.package.json
Errors In backend console
message: 'payment - id must be defined'
Logs from admin dash workflow
Error Logs
MolliePaymentProviderService implementation
If you need any additional data, please let me know. Any kind of help will be appreciated. Thank you!
Beta Was this translation helpful? Give feedback.
All reactions