Spring BootとRabbitMQを用いた支払い結果通知システムの実装

RabbitMQ設定クラス


@Configuration
public class PaymentNotificationConfig implements ApplicationContextAware {

    public static final String PAYMENT_EXCHANGE = "payment_exchange";
    public static final String NOTIFICATION_TYPE = "payment_result";
    public static final String NOTIFICATION_QUEUE = "payment_notification_queue";

    @Bean(PAYMENT_EXCHANGE)
    public FanoutExchange paymentExchange() {
        return new FanoutExchange(PAYMENT_EXCHANGE, true, false);
    }

    @Bean(NOTIFICATION_QUEUE)
    public Queue notificationQueue() {
        return QueueBuilder.durable(NOTIFICATION_QUEUE).build();
    }

    @Bean
    public Binding bindNotificationQueue(
        @Qualifier(NOTIFICATION_QUEUE) Queue queue,
        @Qualifier(PAYMENT_EXCHANGE) FanoutExchange exchange
    ) {
        return BindingBuilder.bind(queue).to(exchange);
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);
        MessageService messageService = context.getBean(MessageService.class);
        
        rabbitTemplate.setReturnCallback((message, code, text, exchange, routingKey) -> {
            log.warn("メッセージ配信失敗: コード{}, 原因{}, エクスチェンジ{}, ルーティングキー{}",
                    code, text, exchange, routingKey);
            PaymentMessage paymentMessage = JSON.parseObject(
                message.toString(), PaymentMessage.class);
            messageService.saveMessage(
                paymentMessage.getMessageType(),
                paymentMessage.getBusinessKey1(),
                paymentMessage.getBusinessKey2(),
                paymentMessage.getBusinessKey3()
            );
        });
    }
}

メッセージ送信処理


@Service
public class PaymentNotificationService {

    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    @Autowired
    private MessageService messageService;

    public void sendPaymentNotification(PaymentMessage message) {
        String jsonMessage = JSON.toJSONString(message);
        Message messageObj = MessageBuilder
            .withBody(jsonMessage.getBytes(StandardCharsets.UTF_8))
            .setDeliveryMode(MessageDeliveryMode.PERSISTENT)
            .build();

        CorrelationData correlationData = new CorrelationData(
            message.getId().toString());

        correlationData.getFuture().addCallback(
            result -> {
                if (result.isAck()) {
                    log.debug("通知送信成功: ID{}", correlationData.getId());
                    messageService.removeMessage(message.getId());
                } else {
                    log.error("通知送信失敗: ID{}, 原因{}", 
                        correlationData.getId(), result.getReason());
                }
            },
            ex -> log.error("送信例外: ID{}, 原因{}", 
                correlationData.getId(), ex.getMessage())
        );

        rabbitTemplate.convertAndSend(
            PaymentNotificationConfig.PAYMENT_EXCHANGE,
            "",
            messageObj,
            correlationData
        );
    }
}

アプリケーション設定


spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    publisher-confirm-type: correlated
    publisher-returns: true
    template:
      mandatory: true
    listener:
      simple:
        prefetch: 1
        acknowledge-mode: manual
        retry:
          enabled: true
          initial-interval: 1000ms
          multiplier: 1
          max-attempts: 3
          stateless: true

支払い結果問合せ処理


@Service
public class PaymentQueryService {

    public PaymentRecord queryPaymentResult(String paymentId) {
        AlipayClient client = new DefaultAlipayClient(
            AlipayConfig.URL,
            APP_ID,
            APP_PRIVATE_KEY,
            "json",
            AlipayConfig.CHARSET,
            ALIPAY_PUBLIC_KEY,
            AlipayConfig.SIGNTYPE
        );

        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        JSONObject content = new JSONObject();
        content.put("out_trade_no", paymentId);
        request.setBizContent(content.toString());

        try {
            AlipayTradeQueryResponse response = client.execute(request);
            if (response.isSuccess()) {
                return processPaymentResponse(response, paymentId);
            }
        } catch (AlipayApiException e) {
            log.error("支払い結果問合せエラー: {}", paymentId, e);
        }
        return null;
    }

    private PaymentRecord processPaymentResponse(
        AlipayTradeQueryResponse response, String paymentId) {
        
        Map responseData = JSON.parseObject(
            response.getBody(), Map.class);
        Map tradeData = (Map) responseData.get(
            "alipay_trade_query_response");
        
        PaymentStatus status = new PaymentStatus();
        status.setTradeStatus((String) tradeData.get("trade_status"));
        status.setTradeNo((String) tradeData.get("trade_no"));
        status.setOutTradeNo(paymentId);
        status.setAppId(APP_ID);
        
        return updatePaymentStatus(status);
    }
}

メッセージ消費処理


@Service
public class PaymentMessageConsumer {

    @RabbitListener(queues = PaymentNotificationConfig.NOTIFICATION_QUEUE)
    public void consumeMessage(Message message, Channel channel) {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        String messageBody = new String(message.getBody());
        PaymentMessage paymentMessage = JSON.parseObject(
            messageBody, PaymentMessage.class);

        try {
            processCourseSelection(
                paymentMessage.getBusinessKey1(),
                paymentMessage.getBusinessKey2()
            );
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            handleConsumptionError(channel, deliveryTag, paymentMessage, e);
        }
    }

    private void processCourseSelection(String courseId, String courseType) {
        CourseSelection selection = courseSelectionMapper.selectById(courseId);
        if (selection != null && "60201".equals(courseType)) {
            selection.setStatus("701001");
            courseSelectionMapper.updateById(selection);
            initializeCourseTable(selection);
        }
    }
}

タグ: Spring Boot RabbitMQ Message Queue Payment System Alipay Integration

7月22日 23:05 投稿