bitpay / java-bitpay-client

Java library for the new cryptographically secure BitPay API
MIT License
40 stars 54 forks source link

Request an Invoice Webhook to be Resent - how to make it work? #328

Open jan-domozilov opened 5 days ago

jan-domozilov commented 5 days ago

Hi!

Following API doc (https://developer.bitpay.com/reference/request-an-invoice-webhook-to-be-resent) it is clear that in order to replay webhook event the respective request needs to contain a token. It is also clear that it is not a client token (merchant token) but resource specific token. So far so good.

Now, let's create BitPay client

public Client getBitpayApi() throws BitPayGenericException {

        TokenContainer tokens = new TokenContainer();
        tokens.addMerchant(bitPayMerchantToken);

        return new Client(Environment.PROD, new PrivateKey(bitPayPrivateKey), tokens, (HttpHost)null, (CredentialsProvider)null);

}

In all the code below client created above is referred as bitPayApi.

Great, and now let's retrieve Invoice

Invoice invoice = bitpayApi.getInvoice(invoiceId);
String invoiceSpecificToken = invoice.getToken();

Okay, invoiceSpecificToken value retrieved is different from my bitPayMerchantToken, so seems like it is resource specific token.

Now, let's do this (but strange, where do I specify invoice specific token? Hmm ... )

bitpayApi.requestInvoiceWebhookToBeResent(invoiceId);

But no, error is returned: "This endpoint does not support the merchant facade". Hmmm ...

Okay, let's see how bitpayApi actually does the request (source code from InvoiceClient of SDK):

public Boolean requestInvoiceWebhookToBeResent(String invoiceId) throws BitPayApiException, BitPayGenericException {
        Map<String, String> 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 var7) {
            BitPayExceptionProvider.throwEncodeException(var7.getMessage());
        }

        HttpResponse response = this.bitPayClient.post("invoices/" + invoiceId + "/notifications", json);
        String jsonResponse = ResponseParser.getJsonDataFromJsonResponse(response.getBody());
        return jsonResponse.replace("\"", "").toLowerCase(Locale.ROOT).equals("success");
    }

So, it puts MERCHANT token as token into the request. How do I pass resource specific token then?

Okay, let's try very dirty way - let's create a new client with Merchant token equal to Invoice token - just to see if it works:

Invoice invoice = bitpayApi.getInvoice(invoiceId);
String invoiceSpecificToken = invoice.getToken();

TokenContainer tokens = new TokenContainer();
tokens.addMerchant(invoiceSpecificToken);
Client invoiceSpecificClient = new Client(Environment.PROD, new PrivateKey(bitPayPrivateKey), tokens, (HttpHost)null, (CredentialsProvider)null);
invoiceSpecificClient.requestInvoiceWebhookToBeResent(invoiceId);

Nope, same error: "This endpoint does not support the merchant facade".

Okay, maybe I can add some other token to TokenContainer? Yes, I can - use put method. Okay. But how do I make requestInvoiceWebhookToBeResent use it?

I am obviously doing something wrong, please guide me.

My two questions are:

  1. How do I properly pass resource specific token to BitPay SDK in general and specifically for requestInvoiceWebhookToBeResent method?
  2. Why my dirty way didn't work? What am I missing? Is it that API determines my request as merchant faced because requestInvoiceWebhookToBeResent by default passes x-identity and x-signature headers, that is why my dirty trick doesn't work?
bobbrodie commented 4 days ago

Hi @jan-domozilov I'm taking a look at this with the API team and we'll report back. I believe we're going to make a small update to the SDK.