@Transactional voidcreatePayment(PaymentCreateReq req){ try { // idempotency boolean inserted = idempotencyService.insert(buildIdempotencyReq(req)); // leverage db unique idx if (!inserted) { log.warn("another instance is creating this payment, skip"); return; } var swiftPayload = parseSwift(readFileContent(req.swiftFile)); var payment = savePayment(swiftPayload, "sp_initiate_payment"); // stored-procedure savePaymentOutbox(payment, PaymentAction.MOVE_FILE_TO_PROCESSED); } catch (PaymentCreateException ex) { handlePaymentCreationException(req, ex); } }
voidhandlePaymentCreationException(PaymentCreateReq req, PaymentCreateException ex){ var errCode = ex.getCode(); switch errCode: case ErrCode.ParseErr: log.error("pasring swift failed", ex); // email alert based on log. savePaymentOutbox(payment, PaymentAction.MOVE_FILE_TO_NOT_PROCESSED); break; case ErrCode.CreateErr: log.error("save payment failed", ex); // alert & should be fixed case by case. throw ex; }
@Scheduled(fixedDelay= 300*1000) @SchedulerLock(name = "pmt_creation_outbox_lock", lockAtLeastFor = "1m") voidpaymentOutboxHandler(PaymentOutbox outbox){ var pmtAction = outbox.action; switch pmtAction: case PaymentAction.MOVE_FILE_TO_PROCESSED: case PaymentAction.MOVE_FILE_TO_NOT_PROCESSED: moveFile(outbox); break; default: break; }
by default, Payment is created with Initiated status, we have a seperated screening processor (see OFAC section), once screening approved, payment are able to be released.