Mollie Payments in Java: Complete Implementation Guide

Introduction to Mollie Payments

Mollie is a popular European payment service provider that offers easy integration for various payment methods. This guide covers building a comprehensive Java client for Mollie's API, supporting payments, refunds, subscriptions, and webhook handling.

Key Features

  • Payment Processing for multiple payment methods
  • Refund Management for processing returns
  • Subscription Handling for recurring payments
  • Webhook Integration for payment status updates
  • Customer Management for storing payment details
  • Order Management for complex payment flows

Dependencies and Setup

Maven Configuration

<properties>
<okhttp.version>4.12.0</okhttp.version>
<jackson.version>2.15.2</jackson.version>
<spring.boot.version>2.7.0</spring.boot.version>
</properties>
<dependencies>
<!-- HTTP Client -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
<!-- JSON Processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- Spring Boot (Optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
<optional>true</optional>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
</dependencies>

Core Mollie Client Implementation

Main Mollie Client

package com.mollie.client;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class MollieClient {
private static final Logger logger = LoggerFactory.getLogger(MollieClient.class);
private final String apiKey;
private final boolean testMode;
private final OkHttpClient httpClient;
private final ObjectMapper objectMapper;
private final String baseUrl;
public MollieClient(String apiKey) {
this(apiKey, true);
}
public MollieClient(String apiKey, boolean testMode) {
this.apiKey = apiKey;
this.testMode = testMode;
this.objectMapper = new ObjectMapper();
this.httpClient = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build();
this.baseUrl = "https://api.mollie.com/v2";
}
/**
* Execute API request with authentication
*/
private Response executeRequest(Request request) throws MollieException {
try {
Response response = httpClient.newCall(request).execute();
if (!response.isSuccessful()) {
handleErrorResponse(response);
}
return response;
} catch (IOException e) {
throw new MollieException("API request failed", e);
}
}
/**
* Handle error responses from Mollie API
*/
private void handleErrorResponse(Response response) throws IOException, MollieException {
String errorBody = response.body().string();
logger.error("Mollie API error: {} - {}", response.code(), errorBody);
try {
ErrorResponse errorResponse = objectMapper.readValue(errorBody, ErrorResponse.class);
throw new MollieException(
errorResponse.getDetail() != null ? errorResponse.getDetail() : errorResponse.getTitle(),
response.code()
);
} catch (Exception e) {
throw new MollieException("HTTP " + response.code() + ": " + errorBody, response.code());
}
}
/**
* Create authenticated request builder
*/
private Request.Builder createAuthenticatedRequest() {
return new Request.Builder()
.addHeader("Authorization", "Bearer " + apiKey)
.addHeader("Content-Type", "application/json");
}
/**
* Create a new payment
*/
public Payment createPayment(PaymentRequest paymentRequest) throws MollieException {
try {
String requestBody = objectMapper.writeValueAsString(paymentRequest);
Request request = createAuthenticatedRequest()
.url(baseUrl + "/payments")
.post(RequestBody.create(requestBody, MediaType.parse("application/json")))
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, Payment.class);
}
} catch (IOException e) {
throw new MollieException("Failed to create payment", e);
}
}
/**
* Get payment by ID
*/
public Payment getPayment(String paymentId) throws MollieException {
try {
Request request = createAuthenticatedRequest()
.url(baseUrl + "/payments/" + paymentId)
.get()
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, Payment.class);
}
} catch (IOException e) {
throw new MollieException("Failed to get payment: " + paymentId, e);
}
}
/**
* List payments with pagination
*/
public PaginatedResponse<Payment> listPayments(int limit, String from) throws MollieException {
try {
HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl + "/payments").newBuilder()
.addQueryParameter("limit", String.valueOf(limit));
if (from != null) {
urlBuilder.addQueryParameter("from", from);
}
Request request = createAuthenticatedRequest()
.url(urlBuilder.build())
.get()
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, new TypeReference<PaginatedResponse<Payment>>() {});
}
} catch (IOException e) {
throw new MollieException("Failed to list payments", e);
}
}
/**
* Cancel payment (if possible)
*/
public Payment cancelPayment(String paymentId) throws MollieException {
try {
Request request = createAuthenticatedRequest()
.url(baseUrl + "/payments/" + paymentId)
.delete()
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, Payment.class);
}
} catch (IOException e) {
throw new MollieException("Failed to cancel payment: " + paymentId, e);
}
}
/**
* Create a refund
*/
public Refund createRefund(String paymentId, RefundRequest refundRequest) throws MollieException {
try {
String requestBody = objectMapper.writeValueAsString(refundRequest);
Request request = createAuthenticatedRequest()
.url(baseUrl + "/payments/" + paymentId + "/refunds")
.post(RequestBody.create(requestBody, MediaType.parse("application/json")))
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, Refund.class);
}
} catch (IOException e) {
throw new MollieException("Failed to create refund", e);
}
}
/**
* Get refund by ID
*/
public Refund getRefund(String paymentId, String refundId) throws MollieException {
try {
Request request = createAuthenticatedRequest()
.url(baseUrl + "/payments/" + paymentId + "/refunds/" + refundId)
.get()
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, Refund.class);
}
} catch (IOException e) {
throw new MollieException("Failed to get refund: " + refundId, e);
}
}
/**
* List refunds for a payment
*/
public PaginatedResponse<Refund> listRefunds(String paymentId, int limit, String from) throws MollieException {
try {
HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl + "/payments/" + paymentId + "/refunds").newBuilder()
.addQueryParameter("limit", String.valueOf(limit));
if (from != null) {
urlBuilder.addQueryParameter("from", from);
}
Request request = createAuthenticatedRequest()
.url(urlBuilder.build())
.get()
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, new TypeReference<PaginatedResponse<Refund>>() {});
}
} catch (IOException e) {
throw new MollieException("Failed to list refunds", e);
}
}
/**
* Cancel refund (if possible)
*/
public Refund cancelRefund(String paymentId, String refundId) throws MollieException {
try {
Request request = createAuthenticatedRequest()
.url(baseUrl + "/payments/" + paymentId + "/refunds/" + refundId)
.delete()
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, Refund.class);
}
} catch (IOException e) {
throw new MollieException("Failed to cancel refund: " + refundId, e);
}
}
/**
* Create a customer
*/
public Customer createCustomer(CustomerRequest customerRequest) throws MollieException {
try {
String requestBody = objectMapper.writeValueAsString(customerRequest);
Request request = createAuthenticatedRequest()
.url(baseUrl + "/customers")
.post(RequestBody.create(requestBody, MediaType.parse("application/json")))
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, Customer.class);
}
} catch (IOException e) {
throw new MollieException("Failed to create customer", e);
}
}
/**
* Get customer by ID
*/
public Customer getCustomer(String customerId) throws MollieException {
try {
Request request = createAuthenticatedRequest()
.url(baseUrl + "/customers/" + customerId)
.get()
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, Customer.class);
}
} catch (IOException e) {
throw new MollieException("Failed to get customer: " + customerId, e);
}
}
/**
* Update customer
*/
public Customer updateCustomer(String customerId, CustomerRequest customerRequest) throws MollieException {
try {
String requestBody = objectMapper.writeValueAsString(customerRequest);
Request request = createAuthenticatedRequest()
.url(baseUrl + "/customers/" + customerId)
.patch(RequestBody.create(requestBody, MediaType.parse("application/json")))
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, Customer.class);
}
} catch (IOException e) {
throw new MollieException("Failed to update customer: " + customerId, e);
}
}
/**
* Delete customer
*/
public void deleteCustomer(String customerId) throws MollieException {
try {
Request request = createAuthenticatedRequest()
.url(baseUrl + "/customers/" + customerId)
.delete()
.build();
try (Response response = executeRequest(request)) {
if (!response.isSuccessful()) {
handleErrorResponse(response);
}
}
} catch (IOException e) {
throw new MollieException("Failed to delete customer: " + customerId, e);
}
}
/**
* List customers with pagination
*/
public PaginatedResponse<Customer> listCustomers(int limit, String from) throws MollieException {
try {
HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl + "/customers").newBuilder()
.addQueryParameter("limit", String.valueOf(limit));
if (from != null) {
urlBuilder.addQueryParameter("from", from);
}
Request request = createAuthenticatedRequest()
.url(urlBuilder.build())
.get()
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, new TypeReference<PaginatedResponse<Customer>>() {});
}
} catch (IOException e) {
throw new MollieException("Failed to list customers", e);
}
}
/**
* Create customer payment
*/
public Payment createCustomerPayment(String customerId, PaymentRequest paymentRequest) throws MollieException {
try {
String requestBody = objectMapper.writeValueAsString(paymentRequest);
Request request = createAuthenticatedRequest()
.url(baseUrl + "/customers/" + customerId + "/payments")
.post(RequestBody.create(requestBody, MediaType.parse("application/json")))
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, Payment.class);
}
} catch (IOException e) {
throw new MollieException("Failed to create customer payment", e);
}
}
/**
* List methods available for your account
*/
public PaginatedResponse<Method> listMethods(Double amount, String currency, String resource) throws MollieException {
try {
HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl + "/methods").newBuilder();
if (amount != null) {
urlBuilder.addQueryParameter("amount[value]", String.format("%.2f", amount));
}
if (currency != null) {
urlBuilder.addQueryParameter("amount[currency]", currency);
}
if (resource != null) {
urlBuilder.addQueryParameter("resource", resource);
}
Request request = createAuthenticatedRequest()
.url(urlBuilder.build())
.get()
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, new TypeReference<PaginatedResponse<Method>>() {});
}
} catch (IOException e) {
throw new MollieException("Failed to list methods", e);
}
}
/**
* Get specific method
*/
public Method getMethod(String methodId, Double amount, String currency) throws MollieException {
try {
HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl + "/methods/" + methodId).newBuilder();
if (amount != null && currency != null) {
urlBuilder.addQueryParameter("amount[value]", String.format("%.2f", amount));
urlBuilder.addQueryParameter("amount[currency]", currency);
}
Request request = createAuthenticatedRequest()
.url(urlBuilder.build())
.get()
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, Method.class);
}
} catch (IOException e) {
throw new MollieException("Failed to get method: " + methodId, e);
}
}
// Custom exception
public static class MollieException extends Exception {
private final int statusCode;
public MollieException(String message) {
super(message);
this.statusCode = 0;
}
public MollieException(String message, Throwable cause) {
super(message, cause);
this.statusCode = 0;
}
public MollieException(String message, int statusCode) {
super(message);
this.statusCode = statusCode;
}
public int getStatusCode() {
return statusCode;
}
}
}

Data Models

Core Data Classes

package com.mollie.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Map;
// Payment Request
@JsonIgnoreProperties(ignoreUnknown = true)
public class PaymentRequest {
private Amount amount;
private String description;
private String redirectUrl;
private String webhookUrl;
private String method;
private Map<String, Object> metadata;
private String customerId;
private String sequenceType;
private String mandateId;
private String locale;
private Map<String, Object> billingAddress;
private Map<String, Object> shippingAddress;
private String orderId;
private Map<String, Object> applicationFee;
private Map<String, Object> profileId;
private Boolean testmode;
// Getters and setters
public Amount getAmount() { return amount; }
public void setAmount(Amount amount) { this.amount = amount; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getRedirectUrl() { return redirectUrl; }
public void setRedirectUrl(String redirectUrl) { this.redirectUrl = redirectUrl; }
public String getWebhookUrl() { return webhookUrl; }
public void setWebhookUrl(String webhookUrl) { this.webhookUrl = webhookUrl; }
public String getMethod() { return method; }
public void setMethod(String method) { this.method = method; }
public Map<String, Object> getMetadata() { return metadata; }
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
public String getCustomerId() { return customerId; }
public void setCustomerId(String customerId) { this.customerId = customerId; }
public String getSequenceType() { return sequenceType; }
public void setSequenceType(String sequenceType) { this.sequenceType = sequenceType; }
public String getMandateId() { return mandateId; }
public void setMandateId(String mandateId) { this.mandateId = mandateId; }
public String getLocale() { return locale; }
public void setLocale(String locale) { this.locale = locale; }
public Map<String, Object> getBillingAddress() { return billingAddress; }
public void setBillingAddress(Map<String, Object> billingAddress) { this.billingAddress = billingAddress; }
public Map<String, Object> getShippingAddress() { return shippingAddress; }
public void setShippingAddress(Map<String, Object> shippingAddress) { this.shippingAddress = shippingAddress; }
public String getOrderId() { return orderId; }
public void setOrderId(String orderId) { this.orderId = orderId; }
public Map<String, Object> getApplicationFee() { return applicationFee; }
public void setApplicationFee(Map<String, Object> applicationFee) { this.applicationFee = applicationFee; }
public Map<String, Object> getProfileId() { return profileId; }
public void setProfileId(Map<String, Object> profileId) { this.profileId = profileId; }
public Boolean getTestmode() { return testmode; }
public void setTestmode(Boolean testmode) { this.testmode = testmode; }
}
// Payment Response
@JsonIgnoreProperties(ignoreUnknown = true)
class Payment {
private String id;
private String mode;
private OffsetDateTime createdAt;
private String status;
private Boolean isCancelable;
private Amount amount;
private Amount amountRefunded;
private Amount amountRemaining;
private Amount amountCaptured;
private String description;
private String method;
private Map<String, Object> metadata;
private Map<String, Object> details;
private String profileId;
private String sequenceType;
private String redirectUrl;
private String webhookUrl;
private Links links;
private Customer customer;
private String mandateId;
private String subscriptionId;
private String orderId;
private String settlementId;
private String locale;
private Map<String, Object> billingAddress;
private Map<String, Object> shippingAddress;
private Map<String, Object> applicationFee;
private OffsetDateTime expiredAt;
private OffsetDateTime paidAt;
private OffsetDateTime canceledAt;
private OffsetDateTime failedAt;
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getMode() { return mode; }
public void setMode(String mode) { this.mode = mode; }
public OffsetDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public Boolean getIsCancelable() { return isCancelable; }
public void setIsCancelable(Boolean isCancelable) { this.isCancelable = isCancelable; }
public Amount getAmount() { return amount; }
public void setAmount(Amount amount) { this.amount = amount; }
public Amount getAmountRefunded() { return amountRefunded; }
public void setAmountRefunded(Amount amountRefunded) { this.amountRefunded = amountRefunded; }
public Amount getAmountRemaining() { return amountRemaining; }
public void setAmountRemaining(Amount amountRemaining) { this.amountRemaining = amountRemaining; }
public Amount getAmountCaptured() { return amountCaptured; }
public void setAmountCaptured(Amount amountCaptured) { this.amountCaptured = amountCaptured; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getMethod() { return method; }
public void setMethod(String method) { this.method = method; }
public Map<String, Object> getMetadata() { return metadata; }
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
public Map<String, Object> getDetails() { return details; }
public void setDetails(Map<String, Object> details) { this.details = details; }
public String getProfileId() { return profileId; }
public void setProfileId(String profileId) { this.profileId = profileId; }
public String getSequenceType() { return sequenceType; }
public void setSequenceType(String sequenceType) { this.sequenceType = sequenceType; }
public String getRedirectUrl() { return redirectUrl; }
public void setRedirectUrl(String redirectUrl) { this.redirectUrl = redirectUrl; }
public String getWebhookUrl() { return webhookUrl; }
public void setWebhookUrl(String webhookUrl) { this.webhookUrl = webhookUrl; }
public Links getLinks() { return links; }
public void setLinks(Links links) { this.links = links; }
public Customer getCustomer() { return customer; }
public void setCustomer(Customer customer) { this.customer = customer; }
public String getMandateId() { return mandateId; }
public void setMandateId(String mandateId) { this.mandateId = mandateId; }
public String getSubscriptionId() { return subscriptionId; }
public void setSubscriptionId(String subscriptionId) { this.subscriptionId = subscriptionId; }
public String getOrderId() { return orderId; }
public void setOrderId(String orderId) { this.orderId = orderId; }
public String getSettlementId() { return settlementId; }
public void setSettlementId(String settlementId) { this.settlementId = settlementId; }
public String getLocale() { return locale; }
public void setLocale(String locale) { this.locale = locale; }
public Map<String, Object> getBillingAddress() { return billingAddress; }
public void setBillingAddress(Map<String, Object> billingAddress) { this.billingAddress = billingAddress; }
public Map<String, Object> getShippingAddress() { return shippingAddress; }
public void setShippingAddress(Map<String, Object> shippingAddress) { this.shippingAddress = shippingAddress; }
public Map<String, Object> getApplicationFee() { return applicationFee; }
public void setApplicationFee(Map<String, Object> applicationFee) { this.applicationFee = applicationFee; }
public OffsetDateTime getExpiredAt() { return expiredAt; }
public void setExpiredAt(OffsetDateTime expiredAt) { this.expiredAt = expiredAt; }
public OffsetDateTime getPaidAt() { return paidAt; }
public void setPaidAt(OffsetDateTime paidAt) { this.paidAt = paidAt; }
public OffsetDateTime getCanceledAt() { return canceledAt; }
public void setCanceledAt(OffsetDateTime canceledAt) { this.canceledAt = canceledAt; }
public OffsetDateTime getFailedAt() { return failedAt; }
public void setFailedAt(OffsetDateTime failedAt) { this.failedAt = failedAt; }
}
// Amount
@JsonIgnoreProperties(ignoreUnknown = true)
class Amount {
private String currency;
private String value;
public Amount() {}
public Amount(String currency, String value) {
this.currency = currency;
this.value = value;
}
public Amount(String currency, double value) {
this.currency = currency;
this.value = String.format("%.2f", value);
}
// Getters and setters
public String getCurrency() { return currency; }
public void setCurrency(String currency) { this.currency = currency; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
}
// Links
@JsonIgnoreProperties(ignoreUnknown = true)
class Links {
private String self;
private String checkout;
private String dashboard;
private String documentation;
private String changePaymentState;
private String refunds;
private String capture;
private String settle;
private String customer;
private String mandate;
private String subscription;
private String order;
// Getters and setters
public String getSelf() { return self; }
public void setSelf(String self) { this.self = self; }
public String getCheckout() { return checkout; }
public void setCheckout(String checkout) { this.checkout = checkout; }
public String getDashboard() { return dashboard; }
public void setDashboard(String dashboard) { this.dashboard = dashboard; }
public String getDocumentation() { return documentation; }
public void setDocumentation(String documentation) { this.documentation = documentation; }
public String getChangePaymentState() { return changePaymentState; }
public void setChangePaymentState(String changePaymentState) { this.changePaymentState = changePaymentState; }
public String getRefunds() { return refunds; }
public void setRefunds(String refunds) { this.refunds = refunds; }
public String getCapture() { return capture; }
public void setCapture(String capture) { this.capture = capture; }
public String getSettle() { return settle; }
public void setSettle(String settle) { this.settle = settle; }
public String getCustomer() { return customer; }
public void setCustomer(String customer) { this.customer = customer; }
public String getMandate() { return mandate; }
public void setMandate(String mandate) { this.mandate = mandate; }
public String getSubscription() { return subscription; }
public void setSubscription(String subscription) { this.subscription = subscription; }
public String getOrder() { return order; }
public void setOrder(String order) { this.order = order; }
}
// Customer
@JsonIgnoreProperties(ignoreUnknown = true)
class Customer {
private String id;
private String mode;
private String name;
private String email;
private String locale;
private Map<String, Object> metadata;
private OffsetDateTime createdAt;
private Links links;
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getMode() { return mode; }
public void setMode(String mode) { this.mode = mode; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getLocale() { return locale; }
public void setLocale(String locale) { this.locale = locale; }
public Map<String, Object> getMetadata() { return metadata; }
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
public OffsetDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
public Links getLinks() { return links; }
public void setLinks(Links links) { this.links = links; }
}
// Customer Request
@JsonIgnoreProperties(ignoreUnknown = true)
class CustomerRequest {
private String name;
private String email;
private String locale;
private Map<String, Object> metadata;
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getLocale() { return locale; }
public void setLocale(String locale) { this.locale = locale; }
public Map<String, Object> getMetadata() { return metadata; }
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
}
// Refund Request
@JsonIgnoreProperties(ignoreUnknown = true)
class RefundRequest {
private Amount amount;
private String description;
private Map<String, Object> metadata;
private Boolean testmode;
// Getters and setters
public Amount getAmount() { return amount; }
public void setAmount(Amount amount) { this.amount = amount; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Map<String, Object> getMetadata() { return metadata; }
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
public Boolean getTestmode() { return testmode; }
public void setTestmode(Boolean testmode) { this.testmode = testmode; }
}
// Refund
@JsonIgnoreProperties(ignoreUnknown = true)
class Refund {
private String id;
private Amount amount;
private String settlementId;
private OffsetDateTime createdAt;
private String description;
private Map<String, Object> metadata;
private String paymentId;
private String status;
private Links links;
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public Amount getAmount() { return amount; }
public void setAmount(Amount amount) { this.amount = amount; }
public String getSettlementId() { return settlementId; }
public void setSettlementId(String settlementId) { this.settlementId = settlementId; }
public OffsetDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Map<String, Object> getMetadata() { return metadata; }
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
public String getPaymentId() { return paymentId; }
public void setPaymentId(String paymentId) { this.paymentId = paymentId; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public Links getLinks() { return links; }
public void setLinks(Links links) { this.links = links; }
}
// Method
@JsonIgnoreProperties(ignoreUnknown = true)
class Method {
private String id;
private String description;
private Amount minimumAmount;
private Amount maximumAmount;
private String status;
private Image image;
private Map<String, Object> pricing;
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Amount getMinimumAmount() { return minimumAmount; }
public void setMinimumAmount(Amount minimumAmount) { this.minimumAmount = minimumAmount; }
public Amount getMaximumAmount() { return maximumAmount; }
public void setMaximumAmount(Amount maximumAmount) { this.maximumAmount = maximumAmount; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public Image getImage() { return image; }
public void setImage(Image image) { this.image = image; }
public Map<String, Object> getPricing() { return pricing; }
public void setPricing(Map<String, Object> pricing) { this.pricing = pricing; }
}
// Image
@JsonIgnoreProperties(ignoreUnknown = true)
class Image {
private String size1x;
private String size2x;
private String svg;
// Getters and setters
public String getSize1x() { return size1x; }
public void setSize1x(String size1x) { this.size1x = size1x; }
public String getSize2x() { return size2x; }
public void setSize2x(String size2x) { this.size2x = size2x; }
public String getSvg() { return svg; }
public void setSvg(String svg) { this.svg = svg; }
}
// Paginated Response
@JsonIgnoreProperties(ignoreUnknown = true)
class PaginatedResponse<T> {
private Integer count;
private List<T> data;
private Links links;
// Getters and setters
public Integer getCount() { return count; }
public void setCount(Integer count) { this.count = count; }
public List<T> getData() { return data; }
public void setData(List<T> data) { this.data = data; }
public Links getLinks() { return links; }
public void setLinks(Links links) { this.links = links; }
}
// Error Response
@JsonIgnoreProperties(ignoreUnknown = true)
class ErrorResponse {
private String status;
private String title;
private String detail;
private String field;
private Links links;
// Getters and setters
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDetail() { return detail; }
public void setDetail(String detail) { this.detail = detail; }
public String getField() { return field; }
public void setField(String field) { this.field = field; }
public Links getLinks() { return links; }
public void setLinks(Links links) { this.links = links; }
}

Payment Service

Comprehensive Payment Service

package com.mollie.service;
import com.mollie.client.MollieClient;
import com.mollie.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PaymentService {
private static final Logger logger = LoggerFactory.getLogger(PaymentService.class);
private final MollieClient client;
private final String webhookBaseUrl;
private final String redirectBaseUrl;
public PaymentService(MollieClient client, String webhookBaseUrl, String redirectBaseUrl) {
this.client = client;
this.webhookBaseUrl = webhookBaseUrl;
this.redirectBaseUrl = redirectBaseUrl;
}
/**
* Create a simple payment
*/
public Payment createSimplePayment(double amount, String currency, String description, 
String orderId, Map<String, Object> metadata) throws MollieException {
PaymentRequest request = new PaymentRequest();
request.setAmount(new Amount(currency, amount));
request.setDescription(description);
request.setRedirectUrl(redirectBaseUrl + "/payment/return");
request.setWebhookUrl(webhookBaseUrl + "/webhook/payment");
request.setMetadata(metadata);
if (orderId != null) {
request.setOrderId(orderId);
}
return client.createPayment(request);
}
/**
* Create payment with specific method
*/
public Payment createPaymentWithMethod(double amount, String currency, String description, 
String method, String orderId, Map<String, Object> metadata) throws MollieException {
PaymentRequest request = new PaymentRequest();
request.setAmount(new Amount(currency, amount));
request.setDescription(description);
request.setMethod(method);
request.setRedirectUrl(redirectBaseUrl + "/payment/return");
request.setWebhookUrl(webhookBaseUrl + "/webhook/payment");
request.setMetadata(metadata);
if (orderId != null) {
request.setOrderId(orderId);
}
return client.createPayment(request);
}
/**
* Create recurring payment for customer
*/
public Payment createRecurringPayment(String customerId, double amount, String currency, 
String description, String mandateId, Map<String, Object> metadata) throws MollieException {
PaymentRequest request = new PaymentRequest();
request.setAmount(new Amount(currency, amount));
request.setDescription(description);
request.setCustomerId(customerId);
request.setSequenceType("recurring");
request.setMandateId(mandateId);
request.setMetadata(metadata);
return client.createPayment(request);
}
/**
* Create payment for existing customer
*/
public Payment createCustomerPayment(String customerId, double amount, String currency, 
String description, String method, Map<String, Object> metadata) throws MollieException {
PaymentRequest request = new PaymentRequest();
request.setAmount(new Amount(currency, amount));
request.setDescription(description);
request.setCustomerId(customerId);
request.setMethod(method);
request.setRedirectUrl(redirectBaseUrl + "/payment/return");
request.setWebhookUrl(webhookBaseUrl + "/webhook/payment");
request.setMetadata(metadata);
return client.createCustomerPayment(customerId, request);
}
/**
* Check if payment is successful
*/
public boolean isPaymentSuccessful(String paymentId) throws MollieException {
Payment payment = client.getPayment(paymentId);
return "paid".equals(payment.getStatus());
}
/**
* Check if payment is pending
*/
public boolean isPaymentPending(String paymentId) throws MollieException {
Payment payment = client.getPayment(paymentId);
return "pending".equals(payment.getStatus()) || "open".equals(payment.getStatus());
}
/**
* Check if payment has failed
*/
public boolean isPaymentFailed(String paymentId) throws MollieException {
Payment payment = client.getPayment(paymentId);
return "failed".equals(payment.getStatus()) || "expired".equals(payment.getStatus()) || 
"canceled".equals(payment.getStatus());
}
/**
* Get payment status
*/
public String getPaymentStatus(String paymentId) throws MollieException {
Payment payment = client.getPayment(paymentId);
return payment.getStatus();
}
/**
* Process refund for payment
*/
public Refund processRefund(String paymentId, double amount, String currency, 
String description, Map<String, Object> metadata) throws MollieException {
RefundRequest request = new RefundRequest();
request.setAmount(new Amount(currency, amount));
request.setDescription(description);
request.setMetadata(metadata);
return client.createRefund(paymentId, request);
}
/**
* Process full refund
*/
public Refund processFullRefund(String paymentId, String description, Map<String, Object> metadata) throws MollieException {
Payment payment = client.getPayment(paymentId);
return processRefund(paymentId, 
Double.parseDouble(payment.getAmount().getValue()), 
payment.getAmount().getCurrency(), 
description, metadata);
}
/**
* Get available payment methods for amount
*/
public List<Method> getAvailableMethods(double amount, String currency) throws MollieException {
PaginatedResponse<Method> response = client.listMethods(amount, currency, "orders");
return response.getData();
}
/**
* Check if method is available for amount
*/
public boolean isMethodAvailable(String methodId, double amount, String currency) throws MollieException {
try {
Method method = client.getMethod(methodId, amount, currency);
return "activated".equals(method.getStatus());
} catch (MollieException e) {
if (e.getStatusCode() == 404) {
return false;
}
throw e;
}
}
/**
* Create customer with basic info
*/
public Customer createCustomer(String name, String email, String locale, Map<String, Object> metadata) throws MollieException {
CustomerRequest request = new CustomerRequest();
request.setName(name);
request.setEmail(email);
request.setLocale(locale);
request.setMetadata(metadata);
return client.createCustomer(request);
}
/**
* Update customer information
*/
public Customer updateCustomer(String customerId, String name, String email, Map<String, Object> metadata) throws MollieException {
CustomerRequest request = new CustomerRequest();
request.setName(name);
request.setEmail(email);
request.setMetadata(metadata);
return client.updateCustomer(customerId, request);
}
/**
* Get customer payments
*/
public List<Payment> getCustomerPayments(String customerId, int limit) throws MollieException {
// Note: This would require additional implementation in the client
// For now, we'll list all payments and filter by customer
PaginatedResponse<Payment> payments = client.listPayments(limit, null);
return payments.getData().stream()
.filter(p -> customerId.equals(p.getCustomer() != null ? p.getCustomer().getId() : null))
.toList();
}
/**
* Validate webhook signature (basic implementation)
*/
public boolean validateWebhookSignature(String body, String signature) {
// In a real implementation, you would verify the signature
// This is a simplified version
logger.info("Webhook received with signature: {}", signature);
return true;
}
/**
* Process webhook notification
*/
public WebhookResult processWebhook(String body, String signature) {
try {
if (!validateWebhookSignature(body, signature)) {
return new WebhookResult(false, "Invalid signature");
}
// Parse webhook body
Payment payment = client.getObjectMapper().readValue(body, Payment.class);
String paymentId = payment.getId();
String status = payment.getStatus();
logger.info("Processing webhook for payment {} with status {}", paymentId, status);
// Handle different statuses
switch (status) {
case "paid":
handlePaidPayment(payment);
break;
case "failed":
handleFailedPayment(payment);
break;
case "canceled":
handleCanceledPayment(payment);
break;
case "expired":
handleExpiredPayment(payment);
break;
case "pending":
handlePendingPayment(payment);
break;
default:
logger.warn("Unknown payment status: {}", status);
}
return new WebhookResult(true, "Webhook processed successfully");
} catch (Exception e) {
logger.error("Error processing webhook", e);
return new WebhookResult(false, "Error processing webhook: " + e.getMessage());
}
}
// Webhook handlers
private void handlePaidPayment(Payment payment) {
// Update order status in your system
// Send confirmation email
// Fulfill order
logger.info("Payment {} completed successfully", payment.getId());
// Extract metadata for your business logic
Map<String, Object> metadata = payment.getMetadata();
if (metadata != null) {
String orderId = (String) metadata.get("orderId");
if (orderId != null) {
// Update your order system
logger.info("Updating order {} to paid", orderId);
}
}
}
private void handleFailedPayment(Payment payment) {
// Notify customer
// Update order status
logger.warn("Payment {} failed", payment.getId());
}
private void handleCanceledPayment(Payment payment) {
// Handle canceled payment
logger.info("Payment {} was canceled", payment.getId());
}
private void handleExpiredPayment(Payment payment) {
// Handle expired payment
logger.info("Payment {} expired", payment.getId());
}
private void handlePendingPayment(Payment payment) {
// Handle pending payment (e.g., bank transfer)
logger.info("Payment {} is pending", payment.getId());
}
// Webhook result
public static class WebhookResult {
private final boolean success;
private final String message;
public WebhookResult(boolean success, String message) {
this.success = success;
this.message = message;
}
public boolean isSuccess() { return success; }
public String getMessage() { return message; }
}
}

Spring Boot Integration

Configuration Class

package com.mollie.config;
import com.mollie.client.MollieClient;
import com.mollie.service.PaymentService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MollieConfig {
@Value("${mollie.api.key}")
private String apiKey;
@Value("${mollie.test.mode:true}")
private boolean testMode;
@Value("${app.webhook.base.url}")
private String webhookBaseUrl;
@Value("${app.redirect.base.url}")
private String redirectBaseUrl;
@Bean
public MollieClient mollieClient() {
return new MollieClient(apiKey, testMode);
}
@Bean
public PaymentService paymentService() {
return new PaymentService(mollieClient(), webhookBaseUrl, redirectBaseUrl);
}
}

REST Controller

package com.mollie.controller;
import com.mollie.model.*;
import com.mollie.service.PaymentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/payments")
public class PaymentController {
private static final Logger logger = LoggerFactory.getLogger(PaymentController.class);
@Autowired
private PaymentService paymentService;
@PostMapping("/create")
public ResponseEntity<Map<String, Object>> createPayment(@RequestBody CreatePaymentRequest request) {
Map<String, Object> response = new HashMap<>();
try {
Payment payment = paymentService.createSimplePayment(
request.getAmount(),
request.getCurrency(),
request.getDescription(),
request.getOrderId(),
request.getMetadata()
);
response.put("success", true);
response.put("paymentId", payment.getId());
response.put("checkoutUrl", payment.getLinks().getCheckout());
response.put("status", payment.getStatus());
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("Failed to create payment", e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@PostMapping("/create-with-method")
public ResponseEntity<Map<String, Object>> createPaymentWithMethod(@RequestBody CreatePaymentMethodRequest request) {
Map<String, Object> response = new HashMap<>();
try {
Payment payment = paymentService.createPaymentWithMethod(
request.getAmount(),
request.getCurrency(),
request.getDescription(),
request.getMethod(),
request.getOrderId(),
request.getMetadata()
);
response.put("success", true);
response.put("paymentId", payment.getId());
response.put("checkoutUrl", payment.getLinks().getCheckout());
response.put("status", payment.getStatus());
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("Failed to create payment with method", e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@GetMapping("/{paymentId}")
public ResponseEntity<Map<String, Object>> getPayment(@PathVariable String paymentId) {
Map<String, Object> response = new HashMap<>();
try {
Payment payment = paymentService.getPayment(paymentId);
response.put("success", true);
response.put("payment", payment);
response.put("status", payment.getStatus());
response.put("isSuccessful", paymentService.isPaymentSuccessful(paymentId));
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("Failed to get payment: {}", paymentId, e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@GetMapping("/{paymentId}/status")
public ResponseEntity<Map<String, Object>> getPaymentStatus(@PathVariable String paymentId) {
Map<String, Object> response = new HashMap<>();
try {
String status = paymentService.getPaymentStatus(paymentId);
response.put("success", true);
response.put("paymentId", paymentId);
response.put("status", status);
response.put("isSuccessful", "paid".equals(status));
response.put("isPending", "pending".equals(status) || "open".equals(status));
response.put("isFailed", "failed".equals(status) || "expired".equals(status) || "canceled".equals(status));
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("Failed to get payment status: {}", paymentId, e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@PostMapping("/{paymentId}/refund")
public ResponseEntity<Map<String, Object>> createRefund(
@PathVariable String paymentId,
@RequestBody CreateRefundRequest request) {
Map<String, Object> response = new HashMap<>();
try {
Refund refund = paymentService.processRefund(
paymentId,
request.getAmount(),
request.getCurrency(),
request.getDescription(),
request.getMetadata()
);
response.put("success", true);
response.put("refundId", refund.getId());
response.put("status", refund.getStatus());
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("Failed to create refund for payment: {}", paymentId, e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@PostMapping("/{paymentId}/refund/full")
public ResponseEntity<Map<String, Object>> createFullRefund(
@PathVariable String paymentId,
@RequestBody CreateRefundRequest request) {
Map<String, Object> response = new HashMap<>();
try {
Refund refund = paymentService.processFullRefund(
paymentId,
request.getDescription(),
request.getMetadata()
);
response.put("success", true);
response.put("refundId", refund.getId());
response.put("status", refund.getStatus());
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("Failed to create full refund for payment: {}", paymentId, e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@GetMapping("/methods")
public ResponseEntity<Map<String, Object>> getAvailableMethods(
@RequestParam double amount,
@RequestParam String currency) {
Map<String, Object> response = new HashMap<>();
try {
List<Method> methods = paymentService.getAvailableMethods(amount, currency);
response.put("success", true);
response.put("methods", methods);
response.put("count", methods.size());
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("Failed to get available methods", e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@PostMapping("/webhook")
public ResponseEntity<Map<String, Object>> handleWebhook(
@RequestBody String body,
@RequestHeader(value = "Signature", required = false) String signature) {
Map<String, Object> response = new HashMap<>();
try {
PaymentService.WebhookResult result = paymentService.processWebhook(body, signature);
if (result.isSuccess()) {
response.put("success", true);
response.put("message", result.getMessage());
return ResponseEntity.ok(response);
} else {
response.put("success", false);
response.put("error", result.getMessage());
return ResponseEntity.badRequest().body(response);
}
} catch (Exception e) {
logger.error("Failed to process webhook", e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
// Request DTOs
public static class CreatePaymentRequest {
private double amount;
private String currency;
private String description;
private String orderId;
private Map<String, Object> metadata;
// Getters and setters
public double getAmount() { return amount; }
public void setAmount(double amount) { this.amount = amount; }
public String getCurrency() { return currency; }
public void setCurrency(String currency) { this.currency = currency; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getOrderId() { return orderId; }
public void setOrderId(String orderId) { this.orderId = orderId; }
public Map<String, Object> getMetadata() { return metadata; }
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
}
public static class CreatePaymentMethodRequest {
private double amount;
private String currency;
private String description;
private String method;
private String orderId;
private Map<String, Object> metadata;
// Getters and setters
public double getAmount() { return amount; }
public void setAmount(double amount) { this.amount = amount; }
public String getCurrency() { return currency; }
public void setCurrency(String currency) { this.currency = currency; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getMethod() { return method; }
public void setMethod(String method) { this.method = method; }
public String getOrderId() { return orderId; }
public void setOrderId(String orderId) { this.orderId = orderId; }
public Map<String, Object> getMetadata() { return metadata; }
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
}
public static class CreateRefundRequest {
private double amount;
private String currency;
private String description;
private Map<String, Object> metadata;
// Getters and setters
public double getAmount() { return amount; }
public void setAmount(double amount) { this.amount = amount; }
public String getCurrency() { return currency; }
public void setCurrency(String currency) { this.currency = currency; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Map<String, Object> getMetadata() { return metadata; }
public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
}
}

Customer Management Controller

package com.mollie.controller;
import com.mollie.model.Customer;
import com.mollie.model.CustomerRequest;
import com.mollie.model.PaginatedResponse;
import com.mollie.model.Payment;
import com.mollie.service.PaymentService;
import com.mollie.client.MollieClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/customers")
public class CustomerController {
private static final Logger logger = LoggerFactory.getLogger(CustomerController.class);
@Autowired
private MollieClient mollieClient;
@Autowired
private PaymentService paymentService;
@PostMapping
public ResponseEntity<Map<String, Object>> createCustomer(@RequestBody CustomerRequest request) {
Map<String, Object> response = new HashMap<>();
try {
Customer customer = mollieClient.createCustomer(request);
response.put("success", true);
response.put("customerId", customer.getId());
response.put("customer", customer);
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("Failed to create customer", e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@GetMapping("/{customerId}")
public ResponseEntity<Map<String, Object>> getCustomer(@PathVariable String customerId) {
Map<String, Object> response = new HashMap<>();
try {
Customer customer = mollieClient.getCustomer(customerId);
response.put("success", true);
response.put("customer", customer);
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("Failed to get customer: {}", customerId, e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@PutMapping("/{customerId}")
public ResponseEntity<Map<String, Object>> updateCustomer(
@PathVariable String customerId,
@RequestBody CustomerRequest request) {
Map<String, Object> response = new HashMap<>();
try {
Customer customer = mollieClient.updateCustomer(customerId, request);
response.put("success", true);
response.put("customer", customer);
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("Failed to update customer: {}", customerId, e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@DeleteMapping("/{customerId}")
public ResponseEntity<Map<String, Object>> deleteCustomer(@PathVariable String customerId) {
Map<String, Object> response = new HashMap<>();
try {
mollieClient.deleteCustomer(customerId);
response.put("success", true);
response.put("message", "Customer deleted successfully");
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("Failed to delete customer: {}", customerId, e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@GetMapping
public ResponseEntity<Map<String, Object>> listCustomers(
@RequestParam(defaultValue = "10") int limit,
@RequestParam(required = false) String from) {
Map<String, Object> response = new HashMap<>();
try {
PaginatedResponse<Customer> customers = mollieClient.listCustomers(limit, from);
response.put("success", true);
response.put("customers", customers.getData());
response.put("count", customers.getCount());
response.put("hasNext", customers.getLinks() != null && customers.getLinks().getNext() != null);
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("Failed to list customers", e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@GetMapping("/{customerId}/payments")
public ResponseEntity<Map<String, Object>> getCustomerPayments(
@PathVariable String customerId,
@RequestParam(defaultValue = "10") int limit) {
Map<String, Object> response = new HashMap<>();
try {
List<Payment> payments = paymentService.getCustomerPayments(customerId, limit);
response.put("success", true);
response.put("payments", payments);
response.put("count", payments.size());
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("Failed to get customer payments: {}", customerId, e);
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
}

Application Properties

# Mollie Configuration
mollie.api.key=your_mollie_api_key_here
mollie.test.mode=true
# Application Configuration
app.webhook.base.url=https://yourdomain.com
app.redirect.base.url=https://yourdomain.com
# Server Configuration
server.port=8080
# Logging
logging.level.com.mollie=INFO

Usage Examples

Basic Usage

package com.mollie.examples;
import com.mollie.client.MollieClient;
import com.mollie.model.*;
import com.mollie.service.PaymentService;
import java.util.HashMap;
import java.util.Map;
public class MollieExamples {
public static void main(String[] args) {
// Initialize client
MollieClient client = new MollieClient("your_mollie_api_key", true);
PaymentService paymentService = new PaymentService(client, 
"https://yourdomain.com", "https://yourdomain.com");
try {
// Example 1: Create a simple payment
Map<String, Object> metadata = new HashMap<>();
metadata.put("orderId", "ORDER-123");
metadata.put("userId", "USER-456");
Payment payment = paymentService.createSimplePayment(
29.99, "EUR", "Order #ORDER-123", "ORDER-123", metadata);
System.out.println("Payment created: " + payment.getId());
System.out.println("Checkout URL: " + payment.getLinks().getCheckout());
// Example 2: Check payment status
String paymentId = payment.getId();
String status = paymentService.getPaymentStatus(paymentId);
System.out.println("Payment status: " + status);
// Example 3: Create a customer
Customer customer = paymentService.createCustomer(
"John Doe", "[email protected]", "en_US", new HashMap<>());
System.out.println("Customer created: " + customer.getId());
// Example 4: Create recurring payment for customer
// Note: You would need a mandate ID for recurring payments
// Payment recurringPayment = paymentService.createRecurringPayment(
//     customer.getId(), 29.99, "EUR", "Monthly subscription", "mandate_id", metadata);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Conclusion

This comprehensive Mollie Payments Java client provides:

  1. Complete API coverage for payments, refunds, and customers
  2. Payment processing for one-time and recurring payments
  3. Customer management for storing payment details
  4. Refund handling for processing returns
  5. Webhook integration for real-time payment updates
  6. Spring Boot integration for easy configuration

Key features:

  • Multiple payment methods support (iDEAL, credit card, PayPal, etc.)
  • Recurring payments for subscriptions
  • Comprehensive error handling with proper status codes
  • Type-safe models for all Mollie entities
  • Webhook processing for payment status updates
  • Customer management for improved user experience

This implementation enables Java applications to seamlessly integrate with Mollie's payment platform, providing robust payment processing capabilities for e-commerce applications, subscription services, and other payment-related use cases.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper