S SmartDocs
シリーズ: Bridget java 58 行 · 更新日 2026-05-08

IdempotencyStore.java

Bridget/payment-gateway-microservices/services/payment-service/src/main/java/com/checkout/paymentgw/payment/idempotency/IdempotencyStore.java

package com.checkout.paymentgw.payment.idempotency;

import com.checkout.paymentgw.payment.api.dto.PostPaymentResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.Optional;

/**
 * Redis-backed idempotency cache keyed by {@code merchantId + Idempotency-Key}.
 *
 * <p>If two requests with the same key arrive concurrently, the loser's request is
 * also stored with the same key — so they end up returning the same response. For
 * very strict guarantees, replace {@code SET …} with {@code SET … NX} and reject
 * the second writer with {@code 409 Conflict}; we keep it simple here.
 */
@Component
public class IdempotencyStore {

    private static final Duration TTL = Duration.ofHours(24);

    private final StringRedisTemplate redis;
    private final ObjectMapper mapper;

    public IdempotencyStore(StringRedisTemplate redis, ObjectMapper mapper) {
        this.redis = redis;
        this.mapper = mapper;
    }

    public Optional<PostPaymentResponse> lookup(String merchantId, String idempotencyKey) {
        String json = redis.opsForValue().get(key(merchantId, idempotencyKey));
        if (json == null) {
            return Optional.empty();
        }
        try {
            return Optional.of(mapper.readValue(json, PostPaymentResponse.class));
        } catch (JsonProcessingException e) {
            // Cache poisoned. Treat as cache miss; the next response overwrites it.
            return Optional.empty();
        }
    }

    public void store(String merchantId, String idempotencyKey, PostPaymentResponse response) {
        try {
            redis.opsForValue().set(key(merchantId, idempotencyKey),
                    mapper.writeValueAsString(response), TTL);
        } catch (JsonProcessingException e) {
            // Non-fatal — idempotency is a best-effort optimisation.
        }
    }

    private static String key(String merchantId, String idempotencyKey) {
        return "idem:" + merchantId + ":" + idempotencyKey;
    }
}

関連記事

Bridget java 更新日 2026-03-30

PaymentGatewayApplication.java

PaymentGatewayApplication.java — java source code from the Bridget learning materials (Bridget/payment-gateway-challenge-java/src/main/java/com/checkout/payment/gateway/PaymentGatewayApplication.java).

記事を読む →
Bridget java 更新日 2026-03-20

ApplicationConfiguration.java

ApplicationConfiguration.java — java source code from the Bridget learning materials (Bridget/payment-gateway-challenge-java/src/main/java/com/checkout/payment/gateway/configuration/ApplicationConfiguration.java).

記事を読む →
Bridget java 更新日 2026-03-30

RequestResponseLogger.java

RequestResponseLogger.java — java source code from the Bridget learning materials (Bridget/payment-gateway-challenge-java/src/main/java/com/checkout/payment/gateway/configuration/RequestResponseLogger.java).

記事を読む →
Bridget java 更新日 2026-04-14

PaymentGatewayController.java

PaymentGatewayController.java — java source code from the Bridget learning materials (Bridget/payment-gateway-challenge-java/src/main/java/com/checkout/payment/gateway/controller/PaymentGatewayController.java).

記事を読む →
Bridget java 更新日 2026-03-20

PaymentStatus.java

PaymentStatus.java — java source code from the Bridget learning materials (Bridget/payment-gateway-challenge-java/src/main/java/com/checkout/payment/gateway/enums/PaymentStatus.java).

記事を読む →
Bridget java 更新日 2026-03-29

BankErrorException.java

BankErrorException.java — java source code from the Bridget learning materials (Bridget/payment-gateway-challenge-java/src/main/java/com/checkout/payment/gateway/exception/BankErrorException.java).

記事を読む →