com.bitsofproof.btc1k.server.resource.BopShopResource.java Source code

Java tutorial

Introduction

Here is the source code for com.bitsofproof.btc1k.server.resource.BopShopResource.java

Source

/*
 * Copyright 2013 bits of proof zrt.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bitsofproof.btc1k.server.resource;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bitsofproof.btc1k.server.vault.Vault;
import com.bitsofproof.supernode.api.Address;
import com.bitsofproof.supernode.api.BCSAPI;
import com.bitsofproof.supernode.api.BCSAPIException;
import com.bitsofproof.supernode.api.Transaction;
import com.bitsofproof.supernode.api.TransactionInput;
import com.bitsofproof.supernode.api.TransactionOutput;
import com.bitsofproof.supernode.common.ExtendedKey;
import com.bitsofproof.supernode.common.Key;
import com.bitsofproof.supernode.common.ScriptFormat;
import com.bitsofproof.supernode.common.ValidationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.jersey.core.util.Base64;

@Path("/btc1k")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class BopShopResource {
    private static final Logger log = LoggerFactory.getLogger(BopShopResource.class);

    private final ExtendedKey master;

    private final BCSAPI api;
    private static final long FEE = 10000;
    private static final long mB = 100000;
    private final HttpClient client;
    private final int customerId;
    private final String password;

    private final Vault vault;

    public BopShopResource(BCSAPI api, Vault vault, ExtendedKey master, int customerId, String password)
            throws ValidationException {
        this.master = master;
        this.vault = vault;
        this.api = api;
        client = HttpClientBuilder.create().build();
        this.customerId = customerId;
        this.password = password;

        log.info("Vault address is " + vault.getVaultAddress());
    }

    @Path("/payment")
    @POST
    public void callback(BopShopCallback callback) {
        if (callback.getReason() == BopShopCallbackReason.SETTLED) {
            processRequest(callback.getPaymentRequestId());
        }
    }

    public void processRequest(String requestId) {
        try {
            BopShopPaymentRequest request = retrieveRequest(requestId);

            processParsedRequest(request);
        } catch (BCSAPIException | ValidationException | IOException e) {
            log.error("Error processing ticket payment", e);
        }
    }

    private void processParsedRequest(BopShopPaymentRequest request) throws ValidationException, BCSAPIException {
        Key key = master.getKey(request.getChild());

        Address incomingAddress = Address.fromSatoshiStyle(request.getAddress());
        Transaction transaction = new Transaction();
        transaction.setInputs(new ArrayList<TransactionInput>());
        Set<String> txSet = new HashSet<>();
        long amount = 0;
        for (BopShopEvent event : request.getEvents()) {
            if (event.getEventType() == BopShopEventType.TRANSACTION) {
                if (!txSet.contains(event.getTxHash())) {
                    txSet.add(event.getTxHash());
                    TransactionInput input = new TransactionInput();
                    input.setSourceHash(event.getTxHash());
                    input.setIx(event.getIx());
                    transaction.getInputs().add(input);
                    amount += event.getAmount();
                }
            }
        }

        transaction.setOutputs(new ArrayList<TransactionOutput>());
        TransactionOutput vaultOutput = new TransactionOutput();
        vaultOutput.setScript(vault.getVaultAddress().getAddressScript());
        vaultOutput.setValue(amount - FEE - mB);
        transaction.getOutputs().add(vaultOutput);
        TransactionOutput ticket = new TransactionOutput();
        ticket.setScript(Address.fromSatoshiStyle(request.getTitle()).getAddressScript());
        ticket.setValue(mB);
        transaction.getOutputs().add(ticket);

        int i = 0;
        for (TransactionInput input : transaction.getInputs()) {
            ScriptFormat.Writer sw = new ScriptFormat.Writer();
            byte[] sig = key.sign(
                    transaction.hashTransaction(i++, ScriptFormat.SIGHASH_ALL, incomingAddress.getAddressScript()));
            byte[] sigPlusType = new byte[sig.length + 1];
            System.arraycopy(sig, 0, sigPlusType, 0, sig.length);
            sigPlusType[sigPlusType.length - 1] = (byte) (ScriptFormat.SIGHASH_ALL & 0xff);
            sw.writeData(sigPlusType);
            sw.writeData(key.getPublic());
            input.setScript(sw.toByteArray());
        }
        transaction.computeHash();
        log.info("Ticket issued with " + transaction.getHash() + " for " + request.getId() + " address "
                + request.getTitle());
        api.sendTransaction(transaction);
    }

    public BopShopResource processCleared()
            throws JsonParseException, JsonMappingException, ClientProtocolException, UnsupportedEncodingException,
            IOException, ValidationException, BCSAPIException {
        HttpGet get = new HttpGet("https://api.bitsofproof.com/mbs/1/paymentRequest?state=CLEARED");
        ObjectMapper mapper = new ObjectMapper();
        try {
            BopShopRequestList list = mapper.readValue(executeGet(get), BopShopRequestList.class);
            if (list.get_embedded() != null) {
                Object item = list.get_embedded().get("item");
                if (item instanceof List) {
                    for (Map<String, Object> o : (ArrayList<Map<String, Object>>) item) {
                        processOneCleared(o);
                    }
                } else {
                    processOneCleared((Map<String, Object>) item);
                }
            }
        } catch (Exception e) {
            log.warn("Can not get cleared requests list", e);
        }
        return this;
    }

    private void processOneCleared(Map<String, Object> one) throws ClientProtocolException,
            UnsupportedEncodingException, IOException, ValidationException, BCSAPIException {
        String href = ((String) ((Map<String, Object>) (((Map<String, Object>) one.get("_links")).get("self")))
                .get("href"));
        HttpGet get = new HttpGet(href);
        String req = executeGet(get);
        ObjectMapper mapper = new ObjectMapper();
        processParsedRequest(mapper.readValue(req, BopShopPaymentRequest.class));
    }

    public BopShopPaymentRequest retrieveRequest(String id) throws IOException {
        HttpGet get = new HttpGet("https://api.bitsofproof.com/mbs/1/paymentRequest/" + id);
        String req = executeGet(get);
        ObjectMapper mapper = new ObjectMapper();
        return mapper.readValue(req, BopShopPaymentRequest.class);
    }

    private String executeGet(HttpGet get)
            throws IOException, ClientProtocolException, UnsupportedEncodingException {
        String authorizationString = "Basic "
                + new String(Base64.encode((customerId + ":" + password).getBytes()), "US-ASCII");
        get.setHeader("Authorization", authorizationString);
        HttpResponse response = client.execute(get);
        BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
        StringWriter writer = new StringWriter();
        String line;
        while ((line = in.readLine()) != null) {
            writer.write(line);
        }
        String req = writer.toString();
        return req;
    }

    @Path("/transactions")
    public TransactionsResource tansactions() {
        return new TransactionsResource(vault, api);
    }
}