SDKsJavaSpring Boot

Spring Boot Integration

The Java SDK provides comprehensive Spring Boot integration with auto-configuration, annotations, and reactive support.

Auto-Configuration

Add configuration to application.yml:

t402:
  enabled: true
  facilitator-url: https://facilitator.t402.io
  network: eip155:8453
  pay-to: "0xYourReceiverAddress"
  asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
  token-name: "USD Coin"
  token-version: "2"

The PaymentFilter is automatically registered for /api/* endpoints.

@RequirePayment Annotation

Use annotations for fine-grained payment protection:

import io.t402.spring.RequirePayment;
 
@RestController
public class ApiController {
 
    @RequirePayment(amount = "$0.01", description = "Premium API access")
    @GetMapping("/api/premium")
    public Map<String, String> premiumContent() {
        return Map.of("data", "Premium content!");
    }
 
    @RequirePayment(amount = "1000000", description = "Heavy computation")
    @PostMapping("/api/compute")
    public ComputeResult compute(@RequestBody ComputeRequest request) {
        return computeService.process(request);
    }
}

Class-Level Annotation

@RequirePayment(amount = "$0.05")
@RestController
@RequestMapping("/api/v2")
public class V2ApiController {
    // All endpoints require $0.05
}

Amount Formats

FormatExampleDescription
Dollar notation"$1.00", "$0.01"Parsed as USD
Atomic units"1000000"6 decimals for USDC
Decimal notation"1.00"Converted to atomic

Route-Based Configuration

Configure routes via YAML instead of annotations:

t402:
  enabled: true
  facilitator-url: https://facilitator.t402.io
  network: eip155:8453
  pay-to: "0xYourAddress"
  routes:
    - path: "GET /api/data"
      amount: "$0.01"
      description: "Data access"
    - path: "POST /api/compute"
      amount: "$0.10"
      description: "Heavy computation"
    - path: "/api/premium/**"
      amount: "$0.05"
      description: "Premium endpoints"

Spring WebFlux (Reactive)

For reactive Spring WebFlux applications:

import io.t402.spring.reactive.PaymentWebFilter;
 
@Configuration
@EnableWebFlux
public class WebFluxConfig {
 
    @Bean
    public PaymentWebFilter paymentWebFilter(FacilitatorClient facilitator) {
        return new PaymentWebFilter(facilitator, routeConfigs);
    }
}

Or use auto-configuration:

t402:
  enabled: true
  webflux: true
  facilitator-url: https://facilitator.t402.io

Custom PaymentVerifier

Implement custom verification logic:

import io.t402.server.PaymentVerifier;
 
public class CustomPaymentVerifier implements PaymentVerifier {
 
    private final FacilitatorClient facilitator;
    private final RateLimiter rateLimiter;
 
    public CustomPaymentVerifier(FacilitatorClient facilitator) {
        this.facilitator = facilitator;
        this.rateLimiter = new RateLimiter(100, Duration.ofMinutes(1));
    }
 
    @Override
    public VerificationResponse verify(PaymentPayload payload, PaymentRequirements requirements) {
        // Rate limiting
        if (!rateLimiter.tryAcquire(payload.getFrom())) {
            return VerificationResponse.invalid("Rate limit exceeded");
        }
 
        // Custom validation
        if (isBlacklisted(payload.getFrom())) {
            return VerificationResponse.invalid("Payer is blacklisted");
        }
 
        // Delegate to facilitator
        return facilitator.verify(payload, requirements);
    }
}

Register with Spring Boot:

@Configuration
public class T402Config {
 
    @Bean
    public PaymentVerifier paymentVerifier(FacilitatorClient facilitator) {
        return new CustomPaymentVerifier(facilitator);
    }
}

Dynamic Payment Requirements

Customize requirements per-request:

import io.t402.server.PaymentRequirementsProvider;
 
@Component
public class DynamicPaymentProvider implements PaymentRequirementsProvider {
 
    @Override
    public PaymentRequirements getRequirements(HttpServletRequest request) {
        String tier = getUserTier(request);
 
        BigInteger amount = switch (tier) {
            case "premium" -> BigInteger.valueOf(100_000);
            case "enterprise" -> BigInteger.valueOf(10_000);
            default -> BigInteger.valueOf(1_000_000);
        };
 
        return PaymentRequirements.builder()
            .scheme("exact")
            .network("eip155:8453")
            .amount(amount.toString())
            .asset("0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913")
            .payTo(getPayToAddress(request))
            .build();
    }
}

Configuration Properties

PropertyDescriptionDefault
t402.enabledEnable T402 integrationfalse
t402.facilitator-urlFacilitator service URLhttps://facilitator.t402.io
t402.networkNetwork identifier (CAIP-2)eip155:8453
t402.pay-toPayment receiver address-
t402.assetToken contract address-
t402.token-nameEIP-712 token nameUSD Coin
t402.token-versionEIP-712 token version2
t402.schemePayment schemeexact
t402.max-timeout-secondsMax payment timeout3600
t402.webfluxEnable WebFlux supportfalse

Manual Setup

For non-Spring applications:

import io.t402.server.PaymentFilter;
import io.t402.client.HttpFacilitatorClient;
 
FacilitatorClient facilitator = new HttpFacilitatorClient(
    "https://facilitator.t402.io"
);
 
PaymentFilter filter = new PaymentFilter(facilitator);
 
FilterRegistration.Dynamic registration = servletContext.addFilter("t402", filter);
registration.addMappingForUrlPatterns(
    EnumSet.of(DispatcherType.REQUEST),
    true,
    "/api/*"
);

Testing

Integration Testing

@SpringBootTest
@AutoConfigureMockMvc
class PaymentIntegrationTest {
 
    @Autowired
    MockMvc mockMvc;
 
    @MockBean
    FacilitatorClient facilitator;
 
    @Test
    void shouldRequirePayment() throws Exception {
        mockMvc.perform(get("/api/premium"))
            .andExpect(status().isPaymentRequired())
            .andExpect(jsonPath("$.t402Version").value(2));
    }
 
    @Test
    void shouldAcceptValidPayment() throws Exception {
        when(facilitator.verify(any(), any()))
            .thenReturn(VerificationResponse.valid());
        when(facilitator.settle(any(), any()))
            .thenReturn(SettlementResponse.success("0xTxHash"));
 
        mockMvc.perform(get("/api/premium")
                .header("Payment-Signature", validPaymentHeader))
            .andExpect(status().isOk());
    }
}