All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bitpay.sdk.client.InvoiceClient Maven / Gradle / Ivy

Go to download

Full implementation of the BitPay Payment Gateway. This library implements BitPay's Cryptographically Secure RESTful API.

There is a newer version: 10.1.1
Show newest version
/*
 * Copyright (c) 2019 BitPay.
 * All rights reserved.
 */

package com.bitpay.sdk.client;

import com.bitpay.sdk.exceptions.BitPayApiException;
import com.bitpay.sdk.exceptions.BitPayExceptionProvider;
import com.bitpay.sdk.exceptions.BitPayGenericException;
import com.bitpay.sdk.exceptions.BitPayValidationException;
import com.bitpay.sdk.model.Facade;
import com.bitpay.sdk.model.invoice.Invoice;
import com.bitpay.sdk.model.invoice.InvoiceEventToken;
import com.bitpay.sdk.util.GuidGenerator;
import com.bitpay.sdk.util.JsonMapperFactory;
import com.bitpay.sdk.util.ParameterAdder;
import com.bitpay.sdk.util.TokenContainer;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import org.apache.http.message.BasicNameValuePair;

/**
 * The type Invoice client.
 */
public class InvoiceClient implements ResourceClient {

    private static InvoiceClient instance;
    private final BitPayClient bitPayClient;
    private final TokenContainer accessTokens;
    private final GuidGenerator guidGenerator;

    /**
     * Instantiates a new Invoice client.
     *
     * @param bitPayClient  the bit pay client
     * @param accessTokens  the access tokens
     * @param guidGenerator the Guid generator
     */
    private InvoiceClient(
        BitPayClient bitPayClient,
        TokenContainer accessTokens,
        GuidGenerator guidGenerator
    ) {
        this.bitPayClient = bitPayClient;
        this.accessTokens = accessTokens;
        this.guidGenerator = guidGenerator;
    }

    /**
     * Factory method for Invoice Client.
     *
     * @param bitPayClient BitPay Client
     * @param accessTokens Access Tokens
     * @param guidGenerator Guid Generator
     * @return InvoiceClient
     */
    public static InvoiceClient getInstance(
        BitPayClient bitPayClient,
        TokenContainer accessTokens,
        GuidGenerator guidGenerator
    ) {
        if (Objects.isNull(instance)) {
            instance = new InvoiceClient(bitPayClient, accessTokens, guidGenerator);
        }

        return instance;
    }

    /**
     * Create a BitPay invoice.
     *
     * @param invoice     An Invoice object with request parameters defined.
     * @param facade      The facade used to create it.
     * @param signRequest Signed request.
     * @return A BitPay generated Invoice object.
     * @throws BitPayApiException BitPayApiException class
     * @throws BitPayValidationException BitPayValidationException class
     * @throws BitPayGenericException BitPayGenericException class
     */
    public Invoice create(
        Invoice invoice,
        Facade facade,
        Boolean signRequest
    ) throws BitPayApiException, BitPayValidationException, BitPayGenericException {
        if (Objects.isNull(invoice) || Objects.isNull(facade)) {
            BitPayExceptionProvider.throwMissingParameterException();
        }

        invoice.setToken(this.accessTokens.getAccessToken(facade));
        invoice.setGuid(Objects.isNull(invoice.getGuid()) ? this.guidGenerator.execute() : invoice.getGuid());
        JsonMapper mapper = JsonMapperFactory.create();
        String json = null;

        try {
            json = mapper.writeValueAsString(invoice);
        } catch (JsonProcessingException e) {
            BitPayExceptionProvider.throwSerializeResourceException("Invoice", e.getMessage());
        }

        HttpResponse response = this.bitPayClient.post("invoices", json, signRequest);
        String jsonResponse = ResponseParser.getJsonDataFromJsonResponse(response.getBody());

        try {
            invoice = mapper.readerForUpdating(invoice).readValue(jsonResponse);
        } catch (Exception e) {
            BitPayExceptionProvider.throwDeserializeResourceException("Invoice", e.getMessage());
        }

        this.accessTokens.put(invoice.getId(), invoice.getToken());

        return invoice;
    }

    /**
     * Retrieve a BitPay invoice by invoice id using the specified facade.
     * The client must have been previously authorized for the specified facade.
     *
     * @param invoiceId   The id of the invoice to retrieve.
     * @param facade      The facade used to create it.
     * @param signRequest Signed request.
     * @return A BitPay Invoice object.
     * @throws BitPayApiException BitPayApiException class
     * @throws BitPayGenericException BitPayGenericException class
     */
    public Invoice get(
        String invoiceId,
        Facade facade,
        Boolean signRequest
    ) throws BitPayApiException, BitPayGenericException {
        final List params = new ArrayList();
        ParameterAdder.execute(params, "token", this.accessTokens.getAccessToken(facade));

        Invoice invoice = null;

        HttpResponse response = this.bitPayClient.get("invoices/" + invoiceId, params, signRequest);
        String jsonResponse = ResponseParser.getJsonDataFromJsonResponse(response.getBody());

        try {
            invoice = JsonMapperFactory.create().readValue(jsonResponse, Invoice.class);
        } catch (JsonProcessingException e) {
            BitPayExceptionProvider.throwDeserializeResourceException("Invoice", e.getMessage());
        }

        return invoice;
    }

    /**
     * Retrieve a BitPay invoice by guid using the specified facade.
     * The client must have been previously authorized for the specified facade.
     *
     * @param guid        The guid of the invoice to retrieve.
     * @param facade      The facade used to create it.
     * @param signRequest Signed request.
     * @return A BitPay Invoice object.
     * @throws BitPayValidationException BitPayValidationException class
     * @throws BitPayGenericException BitPayGenericException class
     * @throws BitPayApiException BitPayApiException class
     */
    public Invoice getByGuid(
        String guid,
        Facade facade,
        Boolean signRequest
    ) throws BitPayValidationException, BitPayGenericException, BitPayApiException {
        if (Objects.isNull(guid) || Objects.isNull(facade)) {
            BitPayExceptionProvider.throwMissingParameterException();
        }

        final List params = new ArrayList();
        Invoice invoice = null;

        ParameterAdder.execute(params, "token", this.accessTokens.getAccessToken(facade));
        HttpResponse response = this.bitPayClient.get("invoices/guid/" + guid, params, signRequest);
        String jsonResponse = ResponseParser.getJsonDataFromJsonResponse(response.getBody());

        try {
            invoice = JsonMapperFactory.create().readValue(jsonResponse, Invoice.class);
        } catch (JsonProcessingException e) {
            BitPayExceptionProvider.throwDeserializeResourceException("Invoice", e.getMessage());
        }

        return invoice;
    }

    /**
     * Retrieve a collection of BitPay invoices.
     *
     * @param dateStart The first date for the query filter.
     * @param dateEnd   The last date for the query filter.
     * @param status    The invoice status you want to query on.
     * @param orderId   The optional order id specified at time of invoice creation.
     * @param limit     Maximum results that the query will return (useful for paging results).
     * @param offset    Number of results to offset (ex. skip 10 will give you results starting with the 11th
     * @return A list of BitPay Invoice objects.
     * @throws BitPayApiException       BitPayApiException class
     * @throws BitPayGenericException BitPayGenericException class
     */
    public List getInvoices(
        String dateStart,
        String dateEnd,
        String status,
        String orderId,
        Integer limit,
        Integer offset
    ) throws BitPayApiException, BitPayGenericException {
        if (Objects.isNull(dateStart) || Objects.isNull(dateEnd)) {
            BitPayExceptionProvider.throwMissingParameterException();
        }

        final List params = new ArrayList();
        ParameterAdder.execute(params, "token", this.accessTokens.getAccessToken(Facade.MERCHANT));
        ParameterAdder.execute(params, "dateStart", dateStart);
        ParameterAdder.execute(params, "dateEnd", dateEnd);
        ParameterAdder.execute(params, "orderId", orderId);
        ParameterAdder.execute(params, "status", status);
        if (Objects.nonNull(limit)) {
            ParameterAdder.execute(params, "limit", limit.toString());
        }
        if (Objects.nonNull(offset)) {
            ParameterAdder.execute(params, "offset", offset.toString());
        }

        List invoices = null;
        HttpResponse response = this.bitPayClient.get("invoices", params);
        String jsonResponse = ResponseParser.getJsonDataFromJsonResponse(response.getBody());

        try {
            invoices = Arrays.asList(JsonMapperFactory.create().readValue(jsonResponse, Invoice[].class));
        } catch (JsonProcessingException e) {
            BitPayExceptionProvider.throwDeserializeResourceException("Invoice", e.getMessage());
        }

        return invoices;
    }

    /**
     * Retrieves a bus token which can be used to subscribe to invoice events.
     *
     * @param invoiceId the id of the invoice for which you want to fetch an event token
     * @return InvoiceEventToken event token
     * @throws BitPayApiException BitPayException
     * @throws BitPayGenericException BitPayGenericException
     */
    public InvoiceEventToken getInvoiceEventToken(String invoiceId) throws BitPayApiException, BitPayGenericException {
        final List params = new ArrayList();
        ParameterAdder.execute(params, "token", this.accessTokens.getAccessToken(Facade.MERCHANT));

        HttpResponse response = this.bitPayClient.get("invoices/" + invoiceId + "/events", params);
        String jsonResponse = ResponseParser.getJsonDataFromJsonResponse(response.getBody());
        InvoiceEventToken result = null;

        try {
            result = JsonMapperFactory.create()
                .readValue(jsonResponse, InvoiceEventToken.class);
        } catch (JsonProcessingException e) {
            BitPayExceptionProvider.throwDeserializeResourceException("Invoice", e.getMessage());
        }

        return result;
    }

    /**
     * Update a BitPay invoice with communication method.
     *
     * @param invoiceId  The id of the invoice to updated.
     * @param buyerSms   The buyer's cell number.
     * @param smsCode    The buyer's received verification code.
     * @param buyerEmail The buyer's email address.
     * @param autoVerify Skip the user verification on sandbox ONLY.
     * @return A BitPay generated Invoice object.
     * @throws BitPayApiException        BitPayApiException class
     * @throws BitPayGenericException BitPayGenericException class
     */
    public Invoice update(
        String invoiceId,
        String buyerSms,
        String smsCode,
        String buyerEmail,
        Boolean autoVerify
    ) throws BitPayApiException, BitPayGenericException {
        if (Objects.isNull(invoiceId)) {
            BitPayExceptionProvider.throwMissingParameterException();
        }

        final Map params = new HashMap<>();
        params.put("token", this.accessTokens.getAccessToken(Facade.MERCHANT));
        validateRequiredField(buyerSms, buyerEmail);
        validateSmsCode(buyerSms, smsCode, autoVerify);

        if (buyerSms != null) {
            params.put("buyerSms", buyerSms);
        }
        if (smsCode != null) {
            params.put("smsCode", smsCode);
        }
        if (buyerEmail != null) {
            params.put("buyerEmail", buyerEmail);
        }
        if (autoVerify != null) {
            params.put("autoVerify", autoVerify);
        }

        JsonMapper mapper = JsonMapperFactory.create();
        String json = null;
        Invoice invoice = null;

        try {
            json = mapper.writeValueAsString(params);
        } catch (JsonProcessingException e) {
            BitPayExceptionProvider.throwEncodeException(e.getMessage());
        }

        HttpResponse response = this.bitPayClient.update("invoices/" + invoiceId, json);
        String jsonResponse = ResponseParser.getJsonDataFromJsonResponse(response.getBody());

        try {
            invoice =
                JsonMapperFactory.create().readValue(jsonResponse, Invoice.class);
        } catch (JsonProcessingException e) {
            BitPayExceptionProvider.throwDeserializeResourceException("Invoice", e.getMessage());
        }

        return invoice;
    }

    /**
     * Pay a BitPay invoice with a mock transaction.
     *
     * @param invoiceId The id of the invoice to updated.
     * @param status    The status of the invoice to be updated, can be "confirmed" or "complete".
     * @return A BitPay generated Invoice object.
     * @throws BitPayApiException        BitPayApiException class
     * @throws BitPayGenericException BitPayGenericException class
     */
    public Invoice pay(
        String invoiceId,
        String status
    ) throws BitPayApiException, BitPayGenericException {
        final Map params = new HashMap<>();
        params.put("token", this.accessTokens.getAccessToken(Facade.MERCHANT));
        if (status != null) {
            params.put("status", status);
        }
        JsonMapper mapper = JsonMapperFactory.create();
        String json = null;
        Invoice invoice = null;

        try {
            json = mapper.writeValueAsString(params);
        } catch (JsonProcessingException e) {
            BitPayExceptionProvider.throwEncodeException(e.getMessage());
        }

        HttpResponse response = this.bitPayClient.update("invoices/pay/" + invoiceId, json);
        String jsonResponse = ResponseParser.getJsonDataFromJsonResponse(response.getBody());

        try {
            invoice =
                JsonMapperFactory.create().readValue(jsonResponse, Invoice.class);
        } catch (JsonProcessingException e) {
            BitPayExceptionProvider.throwDeserializeResourceException("Invoice", e.getMessage());
        }

        return invoice;
    }

    /**
     * Delete a previously created BitPay invoice.
     *
     * @param invoiceId The Id of the BitPay invoice to be canceled.
     * @return A BitPay generated Invoice object.
     * @throws BitPayGenericException BitPayGenericException class
     * @throws BitPayApiException              BitPayApiException class
     */
    public Invoice cancel(String invoiceId) throws BitPayApiException, BitPayGenericException {
        return this.cancel(invoiceId, false);
    }

    /**
     * Delete a previously created BitPay invoice.
     *
     * @param invoiceId   The Id of the BitPay invoice to be canceled.
     * @param forceCancel If 'true' it will cancel the invoice even if no contact information is present.
     * @return A BitPay generated Invoice object.
     * @throws BitPayGenericException BitPayGenericException class
     * @throws BitPayApiException BitPayException class
     */
    public Invoice cancel(
        String invoiceId,
        Boolean forceCancel
    ) throws BitPayApiException, BitPayGenericException {
        final List params = new ArrayList();
        ParameterAdder.execute(params, "token", this.accessTokens.getAccessToken(Facade.MERCHANT));
        if (forceCancel) {
            ParameterAdder.execute(params, "forceCancel", forceCancel.toString());
        }
        Invoice invoice = null;

        HttpResponse response = this.bitPayClient.delete("invoices/" + invoiceId, params);
        String jsonResponse = ResponseParser.getJsonDataFromJsonResponse(response.getBody());

        try {
            invoice =
                JsonMapperFactory.create().readValue(jsonResponse, Invoice.class);
        } catch (JsonProcessingException e) {
            BitPayExceptionProvider.throwDeserializeResourceException("Invoice", e.getMessage());
        }

        return invoice;
    }

    /**
     * Cancel Invoice by Guid.
     *
     * @param guid Guid
     * @param forceCancel Force Cancel
     * @return Invoice
     * @throws BitPayApiException BitPayApiException class
     * @throws BitPayGenericException BitPayGenericException class
     */
    public Invoice cancelByGuid(String guid, Boolean forceCancel) throws BitPayApiException, BitPayGenericException {
        if (Objects.isNull(guid) || Objects.isNull(forceCancel)) {
            BitPayExceptionProvider.throwMissingParameterException();
        }

        final List params = new ArrayList();
        ParameterAdder.execute(params, "token", this.accessTokens.getAccessToken(Facade.MERCHANT));
        if (forceCancel.equals(true)) {
            ParameterAdder.execute(params, "forceCancel", forceCancel.toString());
        }
        Invoice invoice = null;

        HttpResponse response = this.bitPayClient.delete("invoices/guid/" + guid, params);
        String jsonResponse = ResponseParser.getJsonDataFromJsonResponse(response.getBody());

        try {
            invoice =
                JsonMapperFactory.create().readValue(jsonResponse, Invoice.class);
        } catch (JsonProcessingException e) {
            BitPayExceptionProvider.throwDeserializeResourceException("Invoice", e.getMessage());
        }

        return invoice;
    }

    /**
     * Request an Invoice Webhook to be Resent.
     *
     * @param invoiceId Invoice ID
     * @return Boolean
     * @throws BitPayApiException BitPayApiException class
     * @throws BitPayGenericException BitPayGenericException class
     */
    public Boolean requestInvoiceWebhookToBeResent(String invoiceId) throws BitPayApiException, BitPayGenericException {
        final Map params = new HashMap<>();
        params.put("token", this.accessTokens.getAccessToken(Facade.MERCHANT));

        JsonMapper mapper = JsonMapperFactory.create();
        String json = null;

        try {
            json = mapper.writeValueAsString(params);
        } catch (JsonProcessingException e) {
            BitPayExceptionProvider.throwEncodeException(e.getMessage());
        }

        HttpResponse response = this.bitPayClient.post("invoices/" + invoiceId + "/notifications", json);
        String jsonResponse = ResponseParser.getJsonDataFromJsonResponse(response.getBody());

        return jsonResponse.replace("\"", "").toLowerCase(Locale.ROOT).equals("success");
    }

    private void validateRequiredField(
        String buyerSms,
        String buyerEmail
    ) throws BitPayValidationException {
        if (buyerSms == null && buyerEmail == null) {
            BitPayExceptionProvider.throwValidationException(
                "Updating the invoice requires Mobile Phone Number for SMS reception.");
        }

        if (Objects.nonNull(buyerSms) && Objects.nonNull(buyerEmail)) {
            BitPayExceptionProvider.throwValidationException(
                "Updating an invoice will require EITHER an SMS or E-mail)");
        }
    }

    private void validateSmsCode(
        String buyerSms,
        String smsCode,
        Boolean autoVerify
    ) throws BitPayValidationException {
        if (Objects.isNull(autoVerify)) {
            return;
        }

        if (autoVerify) {
            return;
        }

        if (Objects.nonNull(buyerSms) && Objects.nonNull(smsCode)) {
            return;
        }

        if (Objects.isNull(buyerSms) && Objects.isNull(smsCode)) {
            return;
        }

        BitPayExceptionProvider.throwValidationException(
            "If provided alongside a valid SMS, will bypass the need to complete an SMS challenge");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy