It’s funny how a seemingly meaningless event in one’s life can lead to an unexpected change. For me, one of those events happened in July 2021 when my flight home was delayed by so much that I paid for my very first Uber.
I was so impressed by the experience that I wrote about the underlying payment processor in my “Leveraging Marqeta to Build a Payment Service in Spring Boot: A How-To Guide” article. I continued to dive deeper into the Marqeta platform, writing about how to create a rewards card and even an article about building a buy now, pay later payment solution.
It was at that point I started talking with Marqeta about becoming a part of their engineering team. About six weeks later, I hung up my consultant hat and started as a full-time employee of a Fintech-based market disruptor that has created the world’s first modern card issuing platform.
It’s been an exciting journey along a challenging, winding road. I love it!
In my spare time, I have continued to dive deeper into the world of web3, and I am always eager to learn more about Fintech too. So exploring where web3 and financial services intersect is natural for me!
For this article, I wanted to see how easy it is for a web2 developer to use Java in order to perform some Fintech transactions using web3 and USDC over the Ethereum blockchain. My plan is to use the Circle Java SDK, Java 17, and a Spring Boot 3 RESTful API.
We can’t talk about USDC (USD Coin) without first talking about Circle, since they are the company that manages the stablecoin. Circle was founded in 2013 (around the same time as Marqeta) with a focus on peer-to-peer payments.
As a private company, Circle has reached some key milestones:
USDC is a digital (crypto) currency pegged to (and backed by) the US dollar. Basically, this means that 1 USDC is always equal to 1 US dollar.
So why USDC then instead of US dollars?
USDC is issued by a private entity (it’s not a central bank digital currency) and is primarily available as an Ethereum ERC-20 token.
With USDC, Circle aims to be a disruptor by giving customers the ability to avoid bank hours, processing times and costly fees — all while building a business with the USDC digital currency.
For this publication, let’s assume your service caters to those interested in using USDC to perform financial transactions. From what you’ve observed, the current fees associated with using the Circle platform will allow your service to still be profitable.
Still trying to keep this scenario realistic, let’s also assume that your infrastructure has its roots in Java-based microservices written in Spring Boot. Your infrastructure supports a proven infrastructure of web2 applications and services.
To keep things simple, we will introduce a new service — called circle-sdk-demo
— which will act as the integration with the Circle platform. As a result, we will plan to explore the Java SDK by Circle — which is still currently in a beta state.
Before getting started with the service, we need to navigate to the Circle’s Developer site and obtain an API key for sandbox use:
https://app-sandbox.circle.com/signup/sandbox
All I had to do is fill out this form:
And then I received an API key for their sandbox:
Keep the API key value handy, as it will be required before we start our new service.
For this demonstration, I thought I would plan to use Spring Boot 3 for the first time. While I used the Spring Initializr in IntelliJ IDEA, the results of my dependencies are noted in the following pom.xml
file for the circle-sdk-demo
service:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.circle</groupId>
<artifactId>circle</artifactId>
<version>0.1.0-beta.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
In order to externalize the API key value, the CircleConfigurationProperties
class was created:
@Data
@Validated
@Configuration("circleConfigurationProperties")
@ConfigurationProperties("circle")
public class CircleConfigurationProperties {
@NotBlank()
private String apiKey;
}
For reference, here is a copy of my application.yaml
configuration file:
circle:
api-key:
server:
port: 8585
error:
include-message: always
You can use this link to determine how to externalize the population of the circle.api-key
value with the API key created above. I certainly do not recommend populating the API key value directly into the configuration file above.
Next, I created the following CircleConfiguration
:
@Slf4j
@RequiredArgsConstructor
@Component
public class CircleConfiguration {
private final CircleConfigurationProperties circleConfigurationProperties;
@Bean
public Circle getCircle() {
log.info("=======================");
log.info("Initializing Circle SDK");
log.info("=======================");
log.info("basePath={}", Circle.SANDBOX_BASE_URL);
log.info("circle.api-key={}",
SecurityUtils.maskCredentialsRevealPrefix(
circleConfigurationProperties.getApiKey(), 7, '*'));
Circle circle = Circle.getInstance()
.setBasePath(Circle.SANDBOX_BASE_URL)
.setApiKey(circleConfigurationProperties.getApiKey());
log.info("circle={}", circle);
log.info("==================================");
log.info("Circle SDK Initialization Complete");
log.info("==================================");
return circle;
}
}
With the API key value set, starting the circle-sdk-demo
Spring Boot 3.x service appears as shown below:
To communicate with the Circle platform, I created a simple pass-through service called CircleIntegrationService
:
@Slf4j
@Service
public class CircleIntegrationService {
private final BalancesApi balancesApi = new BalancesApi();
private final CryptoPaymentIntentsApi cryptoPaymentIntentsApi = new CryptoPaymentIntentsApi();
public ListBalancesResponse getBalances() throws ApiException {
ListBalancesResponse listBalancesResponse = balancesApi.listBalances();
log.info("listBalancesResponse={}", listBalancesResponse);
return listBalancesResponse;
}
public CreatePaymentIntentResponse createPayment(SimplePayment simplePayment) throws ApiException {
CreatePaymentIntentRequest createPaymentIntentRequest = new CreatePaymentIntentRequest(new PaymentIntentCreationRequest()
.idempotencyKey(UUID.randomUUID())
.amount(
new CryptoPaymentsMoney()
.amount(simplePayment.getAmount())
.currency(simplePayment.getCurrency())
)
.settlementCurrency(simplePayment.getSettlementCurrency())
.paymentMethods(
Collections.singletonList(
new PaymentMethodBlockchain()
.chain(Chain.ETH)
.type(PaymentMethodBlockchain.TypeEnum.BLOCKCHAIN)
)
));
CreatePaymentIntentResponse createPaymentIntentResponse = cryptoPaymentIntentsApi.createPaymentIntent(createPaymentIntentRequest);
log.info("createPaymentIntentResponse={}", createPaymentIntentResponse);
return createPaymentIntentResponse;
}
public GetPaymentIntentResponse getPayment(String id) throws ApiException {
UUID paymentIntentId = UUID.fromString(id);
log.info("paymentIntentId={} from id={}", paymentIntentId, id);
GetPaymentIntentResponse getPaymentIntentResponse = cryptoPaymentIntentsApi.getPaymentIntent(paymentIntentId);
log.info("getPaymentIntentResponse={}", getPaymentIntentResponse);
return getPaymentIntentResponse;
}
}
This service allows the following functionality to be performed:
In the example scenario, the circle-sdk-demo
will act as middleware between my existing services and the Circle platform. Next, basic controllers were created for the following URIs:
For this example, I simply created the BalancesController
and PaymentsController
classes to house these URIs. A more realistic design would employ an API First approach, similar to what I noted in my “Exploring the API-First Design Pattern” publication.
With the circle-sdk-demo
service running, I was able to perform some cURL commands against my local service, which interacted with the Circle platform via the Java SDK.
Getting a list of balances:
curl --location 'localhost:8585/balances'
Results in the following payload response from Circle:
{
"data": {
"available": [],
"unsettled": []
}
}
Creating a payment:
curl --location 'localhost:8585/payments' \
--header 'Content-Type: application/json' \
--data '{
"currency" : "USD",
"amount" : "1.67",
"settlement_currency": "USD"
}'
Results in the following payload response from Circle:
{
"data": {
"id": "60b9ff8b-f28c-40cf-9a1c-207d12a5350b",
"amount": {
"amount": "1.67",
"currency": "USD"
},
"amountPaid": {
"amount": "0.00",
"currency": "USD"
},
"amountRefunded": {
"amount": "0.00",
"currency": "USD"
},
"settlementCurrency": "USD",
"paymentMethods": [
{
"type": "blockchain",
"chain": "ETH",
"address": null
}
],
"fees": null,
"paymentIds": [],
"refundIds": [],
"timeline": [
{
"status": "created",
"context": null,
"reason": null,
"time": "2023-03-28T12:26:39.607607Z"
}
],
"expiresOn": null,
"updateDate": "2023-03-28T12:26:39.604637Z",
"createDate": "2023-03-28T12:26:39.604637Z",
"merchantWalletId": "1013833795"
}
}
Getting an existing payment by ID:
curl --location 'localhost:8585/payments/60b9ff8b-f28c-40cf-9a1c-207d12a5350b' \
--header 'Content-Type: application/json'
Results in the following response payload from Circle:
{
"data": {
"id": "60b9ff8b-f28c-40cf-9a1c-207d12a5350b",
"amount": {
"amount": "1.67",
"currency": "USD"
},
"amountPaid": {
"amount": "0.00",
"currency": "USD"
},
"amountRefunded": {
"amount": "0.00",
"currency": "USD"
},
"settlementCurrency": "USD",
"paymentMethods": [
{
"type": "blockchain",
"chain": "ETH",
"address": "0xa7fa0314e4a3f946e9c8a5f404bb9819ed442079"
}
],
"fees": [
{
"type": "blockchainLeaseFee",
"amount": "0.00",
"currency": "USD"
}
],
"paymentIds": [],
"refundIds": [],
"timeline": [
{
"status": "pending",
"context": null,
"reason": null,
"time": "2023-03-28T12:26:42.346901Z"
},
{
"status": "created",
"context": null,
"reason": null,
"time": "2023-03-28T12:26:39.607607Z"
}
],
"expiresOn": "2023-03-28T20:26:42.238810Z",
"updateDate": "2023-03-28T12:26:42.345054Z",
"createDate": "2023-03-28T12:26:39.604637Z",
"merchantWalletId": "1013833795"
}
}
From the Circle developer logs screen, I can see a summary of all of my requests, including the response payload:
Readers of my publications may recall that I have been focused on the following mission statement, which I feel can apply to any IT professional:
“Focus your time on delivering features/functionality that extends the value of your intellectual property. Leverage frameworks, products, and services for everything else.”
- J. Vester
When I look back on each of my web3 publications, I am always taken aback by the number of steps or complexity involved in getting from point A to point B. While I am sure this was the case when I started working with web2, I feel like the learning cost is much higher now. This is where Circle really seems to bridge the gap.
In the example illustrated above, I was able to leverage Java and Spring Boot to integrate a RESTful API into the Circle platform and start making real-time, online, secure payments. As a result, Circle is helping me adhere to my mission statement.
Things tend to move fast in technology fields, and early adopters are often faced with challenges like:
From what I found in this exercise, Circle has avoided these pitfalls – giving me the option to avoid bank hours, processing times, and costly fees – while building my business with the USDC digital currency. In addition to USDC, it also supports card payments, cryptocurrencies, and other digital payment methods. And it has distinct advantages over other payment technologies, such as Apple Pay, PayPal, and Google Pay.
If you are interested in the source code for this publication, it can be found over at GitLab:
https://gitlab.com/johnjvester/circle-sdk-demo
Have a really great day!