How to Prevent Duplicate Order Submissions with Token + Redis Lock in Spring Boot

This article explains why duplicate order submissions occur in e‑commerce systems and presents a comprehensive solution using request tokens, interface idempotency, and Redis‑based distributed locks, complete with Spring Boot code examples and advanced optimization tips.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
How to Prevent Duplicate Order Submissions with Token + Redis Lock in Spring Boot

Background: Why duplicate submissions happen?

In e‑commerce platforms, submitting an order triggers inventory deduction, coupon redemption, payment processing, and message sending. Users often click the submit button twice, refresh a lagging page, or use the browser back button, which leads to repeated order submissions that waste resources and can cause business loss.

Common Scenarios of Duplicate Submissions

Core Principles to Prevent Duplicate Submissions

Address the issue through three aspects: interface idempotency, request uniqueness, and server‑side lock control.

Control a unique request identifier (token/nonce).

Make order operations idempotent.

Introduce cache or distributed lock to limit repeated submissions.

Solution: Token + Redis Lock Design

Design Idea

Frontend obtains a unique token from the server before creating an order.

The token is sent together with the order request.

Backend verifies the token’s existence in Redis.

If verification succeeds, the order logic runs and the token is deleted.

If the token has already been used, the request is rejected as a duplicate.

Code Implementation (Spring Boot + Redis)

1. Frontend token acquisition endpoint

@RestController
@RequestMapping("/api/order")
public class OrderTokenController {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @GetMapping("/token")
    public ResponseEntity<String> getToken() {
        String token = UUID.randomUUID().toString();
        String key = "order:token:" + token;
        // Set validity 5 minutes
        redisTemplate.opsForValue().set(key, "valid", Duration.ofMinutes(5));
        return ResponseEntity.ok(token);
    }
}

2. Order submission endpoint (validate & delete token)

@RestController
@RequestMapping("/api/order")
public class OrderController {
    @Autowired
    private OrderService orderService;

    @PostMapping("/submit")
    public ResponseEntity<String> submitOrder(@RequestBody OrderRequest request,
                                              @RequestHeader("X-Order-Token") String token) {
        boolean success = orderService.submitOrder(request, token);
        if (success) {
            return ResponseEntity.ok("订单提交成功");
        } else {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("请勿重复提交订单");
        }
    }
}

3. OrderService with duplicate‑submission logic

@Service
public class OrderService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * Submit order interface, prevent duplicate submissions
     */
    public boolean submitOrder(OrderRequest request, String token) {
        String redisKey = "order:token:" + token;
        // Atomic delete + check guarantees idempotency
        Boolean result = redisTemplate.delete(redisKey);
        if (Boolean.TRUE.equals(result)) {
            // First submission: token existed and was deleted
            createOrder(request);
            return true;
        } else {
            // Token missing → duplicate submission
            return false;
        }
    }

    private void createOrder(OrderRequest request) {
        // Business logic: generate order number, check stock, deduct stock, persist, send MQ, etc.
        System.out.println("处理订单:" + request);
    }
}

4. OrderRequest DTO

@Data
public class OrderRequest {
    private Long userId;
    private List<Long> productIds;
    private BigDecimal totalAmount;
}

Advanced Optimization Suggestions

1. Use Lua script for atomic Redis operations

Redis delete is not strongly atomic; a Lua script can perform “check‑and‑delete” in a single step.

// Lua script for atomic delete
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                "return redis.call('del', KEYS[1]) else return 0 end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(script);
redisScript.setResultType(Long.class);
Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), "valid");

2. Apply rate‑limiting or circuit‑breaker (e.g., Sentinel) to the order API

Prevent malicious request bursts.

Reduce system pressure caused by duplicate submissions.

3. Database‑level idempotent checks

Even if the application layer fails, unique constraints or INSERT IGNORE / ON DUPLICATE KEY can prevent duplicate rows.

Conclusion

To handle duplicate order submissions, rely on request uniqueness, interface idempotency, and server‑side lock mechanisms. Redis is an effective tool for idempotent control, and a simple token strategy works well for ordering, payment, and flash‑sale scenarios. Multi‑layer defense (application + database) provides the safest protection.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

order processingidempotencyduplicate submission
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.