Sunday, April 18, 2021

Still using Java: Cart, Payment, and Transaction Services

In order to get to the fun part, where we try to integrate all the services, I just quickly draft some requirement and roll them out. We will make changes to it accordingly ni the future.


 


We will create some endpoints for these services, persist the data into a database, that's it.

Create 3 projects using maven command


mvn io.quarkus:quarkus-maven-plugin:1.13.1.Final:create -DprojectGroupId=blog.technodeck -DprojectArtifactId=cart-service -Dverison=0.0.1-SNAPSHOT -Dextensions="resteasy-jackson,jdbc-h2,hibernate-orm-panache"
mvn io.quarkus:quarkus-maven-plugin:1.13.1.Final:create -DprojectGroupId=blog.technodeck -DprojectArtifactId=payment-service -Dverison=0.0.1-SNAPSHOT -Dextensions="resteasy-jackson,jdbc-h2,hibernate-orm-panache"
mvn io.quarkus:quarkus-maven-plugin:1.13.1.Final:create -DprojectGroupId=blog.technodeck -DprojectArtifactId=transaction-service -Dverison=0.0.1-SNAPSHOT -Dextensions="resteasy-jackson,jdbc-h2,hibernate-orm-panache"

then configure the application.properties accordingly.

Cart Service

# configure your datasource
quarkus.datasource.db-kind = h2
quarkus.datasource.username = sa
quarkus.datasource.password = sa
quarkus.datasource.jdbc.url = jdbc:h2:~/cart-database
# drop and create the database at startup (use `update` to only update the schema)
quarkus.hibernate-orm.database.generation = update

Payment Service

# configure your datasource
quarkus.datasource.db-kind = h2
quarkus.datasource.username = sa
quarkus.datasource.password = sa
quarkus.datasource.jdbc.url = jdbc:h2:~/payment-database
# drop and create the database at startup (use `update` to only update the schema)
quarkus.hibernate-orm.database.generation = update

Transaction Service

# configure your datasource
quarkus.datasource.db-kind = h2
quarkus.datasource.username = sa
quarkus.datasource.password = sa
quarkus.datasource.jdbc.url = jdbc:h2:~/transaction-database
# drop and create the database at startup (use `update` to only update the schema)
quarkus.hibernate-orm.database.generation = update

Cart Service Endpoint

We create simple entities.

@Entity
public class Cart extends PanacheEntity {

    public Long userId;
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    public List<CartItem> items;
    public ZonedDateTime createdDate;

}
@Entity
public class CartItem extends PanacheEntity {

    public Long productId;
    public String name;
    public Float price;
    public int quantity;

}

Then the resource class

@Path("/cart")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class CartResource {...}

The resource class can do basic CRUD operation. I won't show the full detail, but there are some interesting API that I found it useful and interesting.

Public functions that mapped to @GET, @PPOST, @PUT, and @DELETE


    Cart cart = (Cart) Cart.find("userId", userId)
                               .firstResultOptional()
                               .orElse(new Cart());

I like this firstResultOptional function from the PanacheQuery class. It returns an Optional which is very handy. Spring Data has this feature too.

Payment Service

Payment Service has a Wallet.

@Entity
public class Wallet extends PanacheEntity {
	
	@NotNull
	@Column(unique = true)
    public Long userId;
	@NotNull
    public Float balance;
	
	@NotBlank
	public String description;
	
}

It also has a few data classes.

public class CardInfo {

	@NotBlank
	public String cardNumber;
	@NotBlank
	public String nameOnCard;
	@NotNull
	public Integer expMonth;
	@NotNull
	public Integer expYear;
	@NotNull
	public Integer ccv;
	
}

//

public class PaymentDetail {

	@NotNull
	public Long walletId;
	@NotNull
	public Float amount;
	public String transactionId;
	public ZonedDateTime createdDate;
	public ZonedDateTime updatedDate;
	public String status;
	
	public PaymentDetail() {}
	
	public PaymentDetail(@NotNull Long walletId, @NotNull Float amount, String transactionId, ZonedDateTime createdDate,
	        ZonedDateTime updatedDate, String status) {
		super();
		this.walletId = walletId;
		this.amount = amount;
		this.transactionId = transactionId;
		this.createdDate = createdDate;
		this.updatedDate = updatedDate;
		this.status = status;
	}
	
}

//

public class TopupInfo {

	@NotNull
	public Long walletId;
	@NotNull
	public Float amount;
	@NotNull
	public CardInfo card;
	
}

In this service, we are experimenting with the Quarkus Validation extension.

        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-hibernate-validator</artifactId>
        </dependency>

By including this dependency, it allows us to validate the request body by simply adding a @Valid annotation in the parameter. See the createWallet() function below. 


Here is the PaymentResource


Let's test the validation by creating a wallet without a required field.


You will get a build-in report. How cool is that?


Lastly Transaction Service

The Entity classes.

@Entity
public class ShippingInfo extends PanacheEntity{

    @NotBlank
    public String recieverName;
    public String company;
    @NotBlank
    public String addressLine1;
    public String addressLine2;
    public String addressLine3;
    public String addressLine4;
    public Integer postcode;
    @NotBlank
    @Column(length = 50)
    public String state;
    @NotBlank
    @Column(length = 50)
    public String country;
    
}
// ---------------------------------
@Entity
public class PurchaseTransaction extends PanacheEntity {

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    @NotNull
    @Valid
    public ShippingInfo shippingInfo;
    @NotNull
    @Column(nullable = false)
    public Long cartId;
    @NotNull
    @Column(nullable = false)
    public Long paymentId;
    @NotNull
    @Column(nullable = false)
    public Double amount;

    public ZonedDateTime createdDate;
    
    @PrePersist
    public void beforePersist() {
        createdDate = ZonedDateTime.now();
    }
}
// ---------------------------------
@Entity
public class WalletTransaction extends PanacheEntity {

    @NotNull
    @Column(nullable = false)
    public Long walletId;
    @NotNull
    @Column(nullable = false)
    public Long userId;
    @NotNull
    @Column(nullable = false)
    public Long txId;
    @NotNull
    @Column(nullable = false)
    public String txType;
    public Double credit;
    public Double debit;

    public ZonedDateTime createdDate;
    
    @PrePersist
    public void beforePersist() {
        createdDate = ZonedDateTime.now();
    }
    
}

The resource endpoint.

@Path("/tx")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class TransactionResource {

    @Transactional
    @POST
    @Path("/purchase")
    public void purchaseTx(@Valid PurchaseTransaction purchaseTransaction) {
        purchaseTransaction.persist();
    }

    @GET
    @Path("/purchase")
    public List<PurchaseTransaction> getPurchaseTransaction() {
        return PurchaseTransaction.findAll(Sort.by("id").ascending()).list();
    }
    
    @Transactional
    @POST
    @Path("/wallet")
    public void walletTx(@Valid WalletTransaction walletTransaction) {
        walletTransaction.persist();   
    }
    
    @GET
    @Path("/wallet")
    public List<WalletTransaction> getWalletTransaction() {
        return WalletTransaction.findAll(Sort.by("id").ascending()).list();
    }
    
}

Making some POST requests



Conclusion

There we have it. Cart, Payment, and Transaction Services written using Quarkus. Currently, each service is simply doing some simple CRUD operation. Here we mainly demonstrate how the code will look like using Quarkus. No doubt it is minimal and amazing. Following post we are going to make these services work together. That is where the fun begins. So, happy reading and stay tuned.

Source code:

https://github.com/devilkazuya99/cart-service

https://github.com/devilkazuya99/payment-service

https://github.com/devilkazuya99/transaction-service





No comments: