This guide provides comprehensive Java implementations for integrating with Square Payments API, including payment processing, customer management, subscription billing, and webhook handling.
Square Payments Overview
Key Features:
- Payment processing (credit cards, digital wallets)
- Customer and card management
- Subscription billing
- Invoice management
- Webhook handling for real-time notifications
Common Use Cases:
- E-commerce payments
- Subscription services
- Point-of-sale systems
- Mobile payments
- Invoice-based billing
Dependencies and Setup
Maven Dependencies
<properties>
<spring-boot.version>3.1.0</spring-boot.version>
<square-java-sdk.version>26.0.0</square-java-sdk.version>
<jackson.version>2.15.2</jackson.version>
</properties>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Square SDK -->
<dependency>
<groupId>com.squareup.sdk</groupId>
<artifactId>square</artifactId>
<version>${square-java-sdk.version}</version>
</dependency>
<!-- JSON Processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- Database -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Utilities -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Application Configuration
# application.yml
app:
square:
# Square API Configuration
environment: "production" # sandbox or production
access-token: "${SQUARE_ACCESS_TOKEN:your_sandbox_token}"
location-id: "${SQUARE_LOCATION_ID:your_location_id}"
application-id: "${SQUARE_APPLICATION_ID:your_app_id}"
# Webhook Configuration
webhook:
signature-key: "${SQUARE_WEBHOOK_SIGNATURE:your_webhook_signature}"
enabled: true
# Payment Configuration
payment:
timeout: 30000
max-retries: 3
currency: "USD"
# Subscription Configuration
subscription:
plan-prefix: "plan_"
trial-period-days: 0
spring:
datasource:
url: jdbc:postgresql://localhost:5432/square_payments
username: postgres
password: password
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
server:
port: 8080
logging:
level:
com.example.squarepayments: DEBUG
com.squareup.sdk: INFO
Core Models
1. Payment Models
// PaymentRequest.java
package com.example.squarepayments.model;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import java.math.BigDecimal;
import java.util.Map;
@Data
public class PaymentRequest {
@NotBlank(message = "Source ID is required")
private String sourceId; // card nonce, customer ID, or card ID
@NotNull(message = "Amount is required")
@Positive(message = "Amount must be positive")
private BigDecimal amount;
private String customerId;
private String locationId;
private String referenceId;
private String note;
private String idempotencyKey;
// Additional payment details
private Boolean autocomplete = true;
private String currency = "USD";
private Map<String, String> metadata;
// Tip amount
private BigDecimal tipAmount;
// Shipping address
private Address shippingAddress;
// Buyer information
private Buyer buyer;
@Data
public static class Address {
private String firstName;
private String lastName;
private String addressLine1;
private String addressLine2;
private String locality;
private String administrativeDistrictLevel1;
private String postalCode;
private String country;
}
@Data
public static class Buyer {
private String emailAddress;
private String phoneNumber;
}
}
// PaymentResponse.java
package com.example.squarepayments.model;
import lombok.Data;
import lombok.Builder;
import java.time.LocalDateTime;
import java.math.BigDecimal;
import java.util.Map;
@Data
@Builder
public class PaymentResponse {
private boolean success;
private String message;
private LocalDateTime timestamp;
private PaymentData payment;
private String squarePaymentId;
private String receiptUrl;
private String status;
@Data
@Builder
public static class PaymentData {
private String id;
private BigDecimal amount;
private String currency;
private String status;
private String sourceType;
private String customerId;
private String orderId;
private String referenceId;
private String note;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private Map<String, String> metadata;
}
public static PaymentResponse success(PaymentData payment) {
return PaymentResponse.builder()
.success(true)
.message("Payment processed successfully")
.timestamp(LocalDateTime.now())
.payment(payment)
.squarePaymentId(payment.getId())
.status(payment.getStatus())
.build();
}
public static PaymentResponse error(String message) {
return PaymentResponse.builder()
.success(false)
.message(message)
.timestamp(LocalDateTime.now())
.build();
}
}
// RefundRequest.java
package com.example.squarepayments.model;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import java.math.BigDecimal;
@Data
public class RefundRequest {
@NotBlank(message = "Payment ID is required")
private String paymentId;
@NotBlank(message = "Idempotency key is required")
private String idempotencyKey;
@NotNull(message = "Amount is required")
@Positive(message = "Amount must be positive")
private BigDecimal amount;
private String reason;
private String tenderId;
}
// RefundResponse.java
package com.example.squarepayments.model;
import lombok.Data;
import lombok.Builder;
import java.time.LocalDateTime;
import java.math.BigDecimal;
@Data
@Builder
public class RefundResponse {
private boolean success;
private String message;
private LocalDateTime timestamp;
private String refundId;
private String status;
private BigDecimal amount;
private String currency;
public static RefundResponse success(String refundId, String status, BigDecimal amount, String currency) {
return RefundResponse.builder()
.success(true)
.message("Refund processed successfully")
.timestamp(LocalDateTime.now())
.refundId(refundId)
.status(status)
.amount(amount)
.currency(currency)
.build();
}
public static RefundResponse error(String message) {
return RefundResponse.builder()
.success(false)
.message(message)
.timestamp(LocalDateTime.now())
.build();
}
}
2. Customer Models
// CustomerRequest.java
package com.example.squarepayments.model;
import lombok.Data;
import jakarta.validation.constraints.Email;
import java.util.Map;
@Data
public class CustomerRequest {
private String givenName;
private String familyName;
@Email(message = "Invalid email format")
private String emailAddress;
private String phoneNumber;
private String companyName;
private String referenceId;
private String note;
private Address address;
private Map<String, String> metadata;
@Data
public static class Address {
private String addressLine1;
private String addressLine2;
private String locality;
private String administrativeDistrictLevel1;
private String postalCode;
private String country;
}
}
// CustomerResponse.java
package com.example.squarepayments.model;
import lombok.Data;
import lombok.Builder;
import java.time.LocalDateTime;
import java.util.Map;
@Data
@Builder
public class CustomerResponse {
private boolean success;
private String message;
private LocalDateTime timestamp;
private CustomerData customer;
@Data
@Builder
public static class CustomerData {
private String id;
private String givenName;
private String familyName;
private String emailAddress;
private String phoneNumber;
private String companyName;
private String referenceId;
private String note;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private Map<String, String> metadata;
private Integer version;
}
public static CustomerResponse success(CustomerData customer) {
return CustomerResponse.builder()
.success(true)
.message("Customer operation successful")
.timestamp(LocalDateTime.now())
.customer(customer)
.build();
}
public static CustomerResponse error(String message) {
return CustomerResponse.builder()
.success(false)
.message(message)
.timestamp(LocalDateTime.now())
.build();
}
}
// CustomerCardRequest.java
package com.example.squarepayments.model;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
@Data
public class CustomerCardRequest {
@NotBlank(message = "Customer ID is required")
private String customerId;
@NotBlank(message = "Card nonce is required")
private String cardNonce;
private String cardholderName;
private String billingAddress;
private String verificationToken;
private Boolean isDefault = false;
}
3. Subscription Models
// SubscriptionPlanRequest.java
package com.example.squarepayments.model;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import java.math.BigDecimal;
import java.util.Map;
@Data
public class SubscriptionPlanRequest {
@NotBlank(message = "Plan name is required")
private String name;
@NotBlank(message = "Plan description is required")
private String description;
@NotNull(message = "Amount is required")
@Positive(message = "Amount must be positive")
private BigDecimal amount;
@NotBlank(message = "Currency is required")
private String currency = "USD";
@NotBlank(message = "Interval is required")
private String interval; // DAILY, WEEKLY, MONTHLY, YEARLY
private Integer intervalCount = 1;
private Map<String, String> metadata;
private Boolean active = true;
}
// SubscriptionRequest.java
package com.example.squarepayments.model;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.util.Map;
@Data
public class SubscriptionRequest {
@NotBlank(message = "Customer ID is required")
private String customerId;
@NotBlank(message = "Plan ID is required")
private String planId;
@NotBlank(message = "Card ID is required")
private String cardId;
private LocalDate startDate;
private String timezone;
private Map<String, String> metadata;
private String idempotencyKey;
// Trial period
private Integer trialPeriodDays;
// Phases for complex subscriptions
private java.util.List<Phase> phases;
@Data
public static class Phase {
private String planId;
private Integer ordinal;
private Integer periods;
private BigDecimal recurringPrice;
private String cadence;
}
}
// SubscriptionResponse.java
package com.example.squarepayments.model;
import lombok.Data;
import lombok.Builder;
import java.time.LocalDateTime;
import java.math.BigDecimal;
import java.util.Map;
@Data
@Builder
public class SubscriptionResponse {
private boolean success;
private String message;
private LocalDateTime timestamp;
private SubscriptionData subscription;
@Data
@Builder
public static class SubscriptionData {
private String id;
private String planId;
private String customerId;
private String status;
private BigDecimal amount;
private String currency;
private String interval;
private LocalDateTime startDate;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private Map<String, String> metadata;
private String currentPhase;
}
public static SubscriptionResponse success(SubscriptionData subscription) {
return SubscriptionResponse.builder()
.success(true)
.message("Subscription created successfully")
.timestamp(LocalDateTime.now())
.subscription(subscription)
.build();
}
public static SubscriptionResponse error(String message) {
return SubscriptionResponse.builder()
.success(false)
.message(message)
.timestamp(LocalDateTime.now())
.build();
}
}
4. Webhook Models
// WebhookEvent.java
package com.example.squarepayments.model;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Map;
@Data
public class WebhookEvent {
private String merchantId;
private String type;
private String eventId;
private LocalDateTime createdAt;
private Map<String, Object> data;
// Common webhook event types
public static class EventType {
public static final String PAYMENT_UPDATED = "payment.updated";
public static final String REFUND_UPDATED = "refund.updated";
public static final String SUBSCRIPTION_UPDATED = "subscription.updated";
public static final String CUSTOMER_CREATED = "customer.created";
public static final String CUSTOMER_UPDATED = "customer.updated";
public static final String INVOICE_UPDATED = "invoice.updated";
}
}
// WebhookNotification.java
package com.example.squarepayments.model;
import lombok.Data;
import java.util.List;
@Data
public class WebhookNotification {
private String merchantId;
private String locationId;
private String type;
private String eventId;
private String createdAt;
private List<WebhookData> data;
@Data
public static class WebhookData {
private String type;
private String id;
private Object object;
}
}
Square Configuration and Client
1. Square Configuration
// SquareConfig.java
package com.example.squarepayments.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "app.square")
public class SquareConfig {
private String environment;
private String accessToken;
private String locationId;
private String applicationId;
private WebhookConfig webhook = new WebhookConfig();
private PaymentConfig payment = new PaymentConfig();
private SubscriptionConfig subscription = new SubscriptionConfig();
@Data
public static class WebhookConfig {
private String signatureKey;
private boolean enabled;
}
@Data
public static class PaymentConfig {
private int timeout;
private int maxRetries;
private String currency;
}
@Data
public static class SubscriptionConfig {
private String planPrefix;
private int trialPeriodDays;
}
}
2. Square Client Service
// SquareClientService.java
package com.example.squarepayments.service;
import com.squareup.sdk.*;
import com.squareup.sdk.api.*;
import com.squareup.sdk.models.*;
import com.example.squarepayments.config.SquareConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
@Service
public class SquareClientService {
private final SquareClient squareClient;
private final PaymentsApi paymentsApi;
private final CustomersApi customersApi;
private final SubscriptionsApi subscriptionsApi;
private final CardsApi cardsApi;
private final RefundsApi refundsApi;
private final LocationsApi locationsApi;
private final ExecutorService executorService;
public SquareClientService(SquareConfig squareConfig) {
Environment environment = squareConfig.getEnvironment().equals("production")
? Environment.PRODUCTION
: Environment.SANDBOX;
this.squareClient = new SquareClient.Builder()
.environment(environment)
.accessToken(squareConfig.getAccessToken())
.userAgentDetail("spring-boot-square-integration/1.0")
.build();
this.paymentsApi = squareClient.getPaymentsApi();
this.customersApi = squareClient.getCustomersApi();
this.subscriptionsApi = squareClient.getSubscriptionsApi();
this.cardsApi = squareClient.getCardsApi();
this.refundsApi = squareClient.getRefundsApi();
this.locationsApi = squareClient.getLocationsApi();
this.executorService = Executors.newFixedThreadPool(10);
log.info("Square client initialized for environment: {}", environment);
}
/**
* Get Square client instance
*/
public SquareClient getSquareClient() {
return squareClient;
}
/**
* Get Payments API
*/
public PaymentsApi getPaymentsApi() {
return paymentsApi;
}
/**
* Get Customers API
*/
public CustomersApi getCustomersApi() {
return customersApi;
}
/**
* Get Subscriptions API
*/
public SubscriptionsApi getSubscriptionsApi() {
return subscriptionsApi;
}
/**
* Get Cards API
*/
public CardsApi getCardsApi() {
return cardsApi;
}
/**
* Get Refunds API
*/
public RefundsApi getRefundsApi() {
return refundsApi;
}
/**
* Get Locations API
*/
public LocationsApi getLocationsApi() {
return locationsApi;
}
/**
* Execute Square API call asynchronously
*/
public <T> CompletableFuture<T> executeAsync(java.util.concurrent.Callable<T> task) {
return CompletableFuture.supplyAsync(() -> {
try {
return task.call();
} catch (Exception e) {
log.error("Async Square API execution failed", e);
throw new RuntimeException("Square API call failed", e);
}
}, executorService);
}
/**
* Convert Money amount to Square Money object
*/
public Money toSquareMoney(java.math.BigDecimal amount, String currency) {
return new Money.Builder()
.amount(amount.movePointRight(2).longValue()) // Convert to cents
.currency(currency)
.build();
}
/**
* Convert Square Money to BigDecimal
*/
public java.math.BigDecimal fromSquareMoney(Money money) {
if (money == null) return java.math.BigDecimal.ZERO;
return java.math.BigDecimal.valueOf(money.getAmount()).movePointLeft(2);
}
/**
* Generate idempotency key
*/
public String generateIdempotencyKey() {
return java.util.UUID.randomUUID().toString();
}
/**
* Validate webhook signature
*/
public boolean validateWebhookSignature(String payload, String signature, String signatureKey) {
try {
// Square webhook signature validation logic
// This is a simplified version - implement proper validation
return true;
} catch (Exception e) {
log.error("Webhook signature validation failed", e);
return false;
}
}
}
Payment Service
// PaymentService.java
package com.example.squarepayments.service;
import com.squareup.sdk.models.*;
import com.example.squarepayments.model.*;
import com.example.squarepayments.config.SquareConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.concurrent.CompletableFuture;
@Slf4j
@Service
@Transactional
public class PaymentService {
private final SquareClientService squareClientService;
private final SquareConfig squareConfig;
public PaymentService(SquareClientService squareClientService, SquareConfig squareConfig) {
this.squareClientService = squareClientService;
this.squareConfig = squareConfig;
}
/**
* Process payment
*/
public PaymentResponse processPayment(PaymentRequest paymentRequest) {
try {
String idempotencyKey = paymentRequest.getIdempotencyKey() != null
? paymentRequest.getIdempotencyKey()
: squareClientService.generateIdempotencyKey();
String locationId = paymentRequest.getLocationId() != null
? paymentRequest.getLocationId()
: squareConfig.getLocationId();
// Create payment request
CreatePaymentRequest request = new CreatePaymentRequest.Builder(
paymentRequest.getSourceId(),
idempotencyKey,
squareClientService.toSquareMoney(paymentRequest.getAmount(), paymentRequest.getCurrency())
)
.locationId(locationId)
.customerId(paymentRequest.getCustomerId())
.referenceId(paymentRequest.getReferenceId())
.note(paymentRequest.getNote())
.autocomplete(paymentRequest.getAutocomplete())
.build();
// Add tip if provided
if (paymentRequest.getTipAmount() != null && paymentRequest.getTipAmount().compareTo(BigDecimal.ZERO) > 0) {
request = request.toBuilder()
.tipMoney(squareClientService.toSquareMoney(paymentRequest.getTipAmount(), paymentRequest.getCurrency()))
.build();
}
// Add metadata if provided
if (paymentRequest.getMetadata() != null && !paymentRequest.getMetadata().isEmpty()) {
request = request.toBuilder()
.metadata(paymentRequest.getMetadata())
.build();
}
// Add shipping address if provided
if (paymentRequest.getShippingAddress() != null) {
Address address = convertToSquareAddress(paymentRequest.getShippingAddress());
request = request.toBuilder()
.shippingAddress(address)
.build();
}
// Add buyer information if provided
if (paymentRequest.getBuyer() != null) {
request = request.toBuilder()
.buyerEmailAddress(paymentRequest.getBuyer().getEmailAddress())
.build();
}
// Execute payment
CompletableFuture<CreatePaymentResponse> future = squareClientService.executeAsync(() ->
squareClientService.getPaymentsApi().createPaymentAsync(request).get()
);
CreatePaymentResponse response = future.get();
if (response.isSuccess()) {
Payment payment = response.getPayment();
log.info("Payment processed successfully: {}", payment.getId());
return PaymentResponse.success(convertToPaymentData(payment));
} else {
log.error("Payment failed: {}", response.getErrors());
return PaymentResponse.error("Payment processing failed: " + response.getErrors().get(0).getDetail());
}
} catch (Exception e) {
log.error("Payment processing failed", e);
return PaymentResponse.error("Payment processing failed: " + e.getMessage());
}
}
/**
* Get payment by ID
*/
public PaymentResponse getPayment(String paymentId) {
try {
CompletableFuture<GetPaymentResponse> future = squareClientService.executeAsync(() ->
squareClientService.getPaymentsApi().getPaymentAsync(paymentId).get()
);
GetPaymentResponse response = future.get();
if (response.isSuccess()) {
Payment payment = response.getPayment();
return PaymentResponse.success(convertToPaymentData(payment));
} else {
log.error("Failed to get payment: {}", response.getErrors());
return PaymentResponse.error("Failed to retrieve payment: " + response.getErrors().get(0).getDetail());
}
} catch (Exception e) {
log.error("Failed to get payment: {}", paymentId, e);
return PaymentResponse.error("Failed to retrieve payment: " + e.getMessage());
}
}
/**
* List payments with filters
*/
public java.util.List<PaymentResponse.PaymentData> listPayments(String customerId, java.time.LocalDateTime beginTime,
java.time.LocalDateTime endTime, String status) {
try {
ListPaymentsRequest.Builder requestBuilder = new ListPaymentsRequest.Builder();
if (customerId != null) {
requestBuilder.customerId(customerId);
}
if (beginTime != null) {
requestBuilder.beginTime(beginTime.toString());
}
if (endTime != null) {
requestBuilder.endTime(endTime.toString());
}
if (status != null) {
requestBuilder.status(status);
}
CompletableFuture<ListPaymentsResponse> future = squareClientService.executeAsync(() ->
squareClientService.getPaymentsApi().listPaymentsAsync(requestBuilder.build()).get()
);
ListPaymentsResponse response = future.get();
if (response.isSuccess()) {
return response.getPayments().stream()
.map(this::convertToPaymentData)
.collect(java.util.stream.Collectors.toList());
} else {
log.error("Failed to list payments: {}", response.getErrors());
throw new RuntimeException("Failed to list payments: " + response.getErrors().get(0).getDetail());
}
} catch (Exception e) {
log.error("Failed to list payments", e);
throw new RuntimeException("Failed to list payments", e);
}
}
/**
* Process refund
*/
public RefundResponse processRefund(RefundRequest refundRequest) {
try {
String idempotencyKey = refundRequest.getIdempotencyKey() != null
? refundRequest.getIdempotencyKey()
: squareClientService.generateIdempotencyKey();
CreateRefundRequest request = new CreateRefundRequest.Builder(
idempotencyKey,
refundRequest.getPaymentId(),
squareClientService.toSquareMoney(refundRequest.getAmount(), "USD")
)
.reason(refundRequest.getReason())
.tenderId(refundRequest.getTenderId())
.build();
CompletableFuture<CreateRefundResponse> future = squareClientService.executeAsync(() ->
squareClientService.getRefundsApi().createRefundAsync(request).get()
);
CreateRefundResponse response = future.get();
if (response.isSuccess()) {
Refund refund = response.getRefund();
log.info("Refund processed successfully: {}", refund.getId());
return RefundResponse.success(
refund.getId(),
refund.getStatus(),
squareClientService.fromSquareMoney(refund.getAmountMoney()),
refund.getAmountMoney().getCurrency()
);
} else {
log.error("Refund failed: {}", response.getErrors());
return RefundResponse.error("Refund processing failed: " + response.getErrors().get(0).getDetail());
}
} catch (Exception e) {
log.error("Refund processing failed", e);
return RefundResponse.error("Refund processing failed: " + e.getMessage());
}
}
/**
* Convert Square Payment to PaymentData
*/
private PaymentResponse.PaymentData convertToPaymentData(Payment payment) {
return PaymentResponse.PaymentData.builder()
.id(payment.getId())
.amount(squareClientService.fromSquareMoney(payment.getAmountMoney()))
.currency(payment.getAmountMoney().getCurrency())
.status(payment.getStatus())
.sourceType(payment.getSourceType())
.customerId(payment.getCustomerId())
.orderId(payment.getOrderId())
.referenceId(payment.getReferenceId())
.note(payment.getNote())
.createdAt(payment.getCreatedAt() != null ? payment.getCreatedAt().toLocalDateTime() : null)
.updatedAt(payment.getUpdatedAt() != null ? payment.getUpdatedAt().toLocalDateTime() : null)
.metadata(payment.getMetadata())
.build();
}
/**
* Convert application Address to Square Address
*/
private Address convertToSquareAddress(PaymentRequest.Address appAddress) {
return new Address.Builder()
.firstName(appAddress.getFirstName())
.lastName(appAddress.getLastName())
.addressLine1(appAddress.getAddressLine1())
.addressLine2(appAddress.getAddressLine2())
.locality(appAddress.getLocality())
.administrativeDistrictLevel1(appAddress.getAdministrativeDistrictLevel1())
.postalCode(appAddress.getPostalCode())
.country(appAddress.getCountry())
.build();
}
/**
* Complete partial payment
*/
public PaymentResponse completePayment(String paymentId) {
try {
CompletableFuture<CompletePaymentResponse> future = squareClientService.executeAsync(() ->
squareClientService.getPaymentsApi().completePaymentAsync(paymentId).get()
);
CompletePaymentResponse response = future.get();
if (response.isSuccess()) {
Payment payment = response.getPayment();
return PaymentResponse.success(convertToPaymentData(payment));
} else {
log.error("Failed to complete payment: {}", response.getErrors());
return PaymentResponse.error("Failed to complete payment: " + response.getErrors().get(0).getDetail());
}
} catch (Exception e) {
log.error("Failed to complete payment: {}", paymentId, e);
return PaymentResponse.error("Failed to complete payment: " + e.getMessage());
}
}
/**
* Cancel payment
*/
public PaymentResponse cancelPayment(String paymentId) {
try {
CompletableFuture<CancelPaymentResponse> future = squareClientService.executeAsync(() ->
squareClientService.getPaymentsApi().cancelPaymentAsync(paymentId).get()
);
CancelPaymentResponse response = future.get();
if (response.isSuccess()) {
Payment payment = response.getPayment();
return PaymentResponse.success(convertToPaymentData(payment));
} else {
log.error("Failed to cancel payment: {}", response.getErrors());
return PaymentResponse.error("Failed to cancel payment: " + response.getErrors().get(0).getDetail());
}
} catch (Exception e) {
log.error("Failed to cancel payment: {}", paymentId, e);
return PaymentResponse.error("Failed to cancel payment: " + e.getMessage());
}
}
}
Customer Service
```java
// CustomerService.java
package com.example.squarepayments.service;
import com.squareup.sdk.models.; import com.example.squarepayments.model.;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.CompletableFuture;
@Slf4j
@Service
@Transactional
public class CustomerService {
private final SquareClientService squareClientService;
public CustomerService(SquareClientService squareClientService) {
this.squareClientService = squareClientService;
}
/**
* Create customer
*/
public CustomerResponse createCustomer(CustomerRequest customerRequest) {
try {
CreateCustomerRequest.Builder requestBuilder = new CreateCustomerRequest.Builder();
if (customerRequest.getGivenName() != null) {
requestBuilder.givenName(customerRequest.getGivenName());
}
if (customerRequest.getFamilyName() != null) {
requestBuilder.familyName(customerRequest.getFamilyName());
}
if (customerRequest.getEmailAddress() != null) {
requestBuilder.emailAddress(customerRequest.getEmailAddress());
}
if (customerRequest.getPhoneNumber() != null) {
requestBuilder.phoneNumber(customerRequest.getPhoneNumber());
}
if (customerRequest.getCompanyName() != null) {
requestBuilder.companyName(customerRequest.getCompanyName());
}
if (customerRequest.getReferenceId() != null) {
requestBuilder.referenceId(customerRequest.getReferenceId());
}
if (customerRequest.getNote() != null) {
requestBuilder.note(customerRequest.getNote());
}
if (customerRequest.getAddress() != null) {
Address address = convertToSquareAddress(customerRequest.getAddress());
requestBuilder.address(address);
}
if (customerRequest.getMetadata() != null) {
requestBuilder.metadata(customerRequest.getMetadata());
}
String idempotencyKey = squareClientService.generateIdempotencyKey();
requestBuilder.idempotencyKey(idempotencyKey);
CompletableFuture<CreateCustomerResponse> future = squareClientService.executeAsync(() ->
squareClientService.getCustomersApi().createCustomerAsync(requestBuilder.build()).get()
);
CreateCustomerResponse response = future.get();
if (response.isSuccess()) {
Customer customer = response.getCustomer();
log.info("Customer created successfully: {}", customer.getId());
return CustomerResponse.success(convertToCustomerData(customer));
} else {
log.error("Failed to create customer: {}", response.getErrors());
return CustomerResponse.error("Failed to create customer: " + response.getErrors().get(0).getDetail());
}
} catch (Exception e) {
log.error("Failed to create customer", e);
return CustomerResponse.error("Failed to create customer: " + e.getMessage());
}
}
/**
* Get customer by ID
*/
public CustomerResponse getCustomer(String customerId) {
try {
CompletableFuture<RetrieveCustomerResponse> future = squareClientService.executeAsync(() ->
squareClientService.getCustomersApi().retrieveCustomerAsync(customerId).get()
);
RetrieveCustomerResponse response = future.get();
if (response.isSuccess()) {
Customer customer = response.getCustomer();
return CustomerResponse.success(convertToCustomerData(customer));
} else {
log.error("Failed to get customer: {}", response.getErrors());
return CustomerResponse.error("Failed to retrieve customer: " + response.getErrors().get(0).getDetail());
}
} catch (Exception e) {
log.error("Failed to get customer: {}", customerId, e);
return CustomerResponse.error("Failed to retrieve customer: " + e.getMessage());
}
}
/**
* Update customer
*/
public CustomerResponse updateCustomer(String customerId, CustomerRequest customerRequest) {
try {
UpdateCustomerRequest.Builder requestBuilder = new UpdateCustomerRequest.Builder();
if (customerRequest.getGivenName() != null) {
requestBuilder.givenName(customerRequest.getGivenName());
}
if (customerRequest.getFamilyName() != null) {
requestBuilder.familyName(customerRequest.getFamilyName());
}
if (customerRequest.getEmailAddress() != null) {
requestBuilder.emailAddress(customerRequest.getEmailAddress());
}
if (customerRequest.getPhoneNumber() != null) {
requestBuilder.phoneNumber(customerRequest.getPhoneNumber());
}
if (customerRequest.getCompanyName() != null) {
requestBuilder.companyName(customerRequest.getCompanyName());
}
if (customerRequest.getReferenceId() != null) {
requestBuilder.referenceId(customerRequest.getReferenceId());
}
if (customerRequest.getNote() != null) {
requestBuilder.note(customerRequest.getNote());
}
if (customerRequest.getAddress() != null) {
Address address = convertToSquareAddress(customerRequest.getAddress());
requestBuilder.address(address);
}
if (customerRequest.getMetadata() != null) {
requestBuilder.metadata(customerRequest.getMetadata());
}
CompletableFuture<UpdateCustomerResponse> future = squareClientService.executeAsync(() ->
squareClientService.getCustomersApi().updateCustomerAsync(customerId, requestBuilder.build()).get()
);
UpdateCustomerResponse response = future.get();
if (response.isSuccess()) {
Customer customer = response.getCustomer();
log.info("Customer updated successfully: {}", customer.getId());
return CustomerResponse.success(convertToCustomerData(customer));
} else {
log