func (c *PaymentProtocolClient) DownloadBip0070PaymentRequest(uri string) (*PaymentRequest, error) {
	u, err := url.Parse(uri)
	if err != nil {
		return nil, err
	}
	endpoint := u.Query().Get("r")
	if endpoint == "" {
		return nil, errors.New("invalid bitcoin cash URI")
	}
	request, err := http.NewRequest(http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	request.Header.Add("Accept", "application/bitcoincash-paymentrequest")
	resp, err := c.httpClient.Do(request)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("http status not OK: %d", resp.StatusCode)
	}
	paymentRequestBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	paymentRequest := new(payments.PaymentRequest)
	if err = proto.Unmarshal(paymentRequestBytes, paymentRequest); err != nil {
		return nil, err
	}
	switch paymentRequest.GetPkiType() {
	case "x509+sha256":
		break
	case "x509+sha1":
		return nil, errors.New("payment request PkiType x509+sha1 is insecure")
	default:
		return nil, errors.New("payment request PkiType unknown")
	}
	certificateProto := new(payments.X509Certificates)
	if err := proto.Unmarshal(paymentRequest.PkiData, certificateProto); err != nil {
		return nil, err
	}
	var certs []*x509.Certificate
	for _, certBytes := range certificateProto.Certificate {
		cert, err := x509.ParseCertificate(certBytes)
		if err != nil {
			return nil, err
		}
		certs = append(certs, cert)
	}
	if len(certs) < 2 {
		return nil, errors.New("invalid number of certs")
	}
	if !c.skipExpirationChecks {
		if time.Now().After(certs[0].NotAfter) {
			return nil, errors.New("certificate is expired")
		}
	}
	if time.Now().Before(certs[0].NotBefore) {
		return nil, errors.New("certificate is not valid yet")
	}
	roots := x509.NewCertPool()
	roots.AddCert(certs[1])
	opts := x509.VerifyOptions{
		Roots: roots,
	}
	if c.skipExpirationChecks {
		opts.CurrentTime = certs[0].NotAfter.Add(-time.Minute)
	}
	if _, err := certs[0].Verify(opts); err != nil {
		return nil, err
	}
	signature := paymentRequest.Signature
	paymentRequest.Signature = []byte{}  
	serializedPaymentRequest, err := proto.Marshal(paymentRequest)
	if err != nil {
		return nil, err
	}
	if err := certs[0].CheckSignature(certs[0].SignatureAlgorithm, serializedPaymentRequest, signature); err != nil {
		return nil, err
	}
	paymentDetails := new(payments.PaymentDetails)
	if err := proto.Unmarshal(paymentRequest.SerializedPaymentDetails, paymentDetails); err != nil {
		return nil, err
	}
	pr := &PaymentRequest{
		PayToName: certs[0].Subject.CommonName,
	}
	for _, out := range paymentDetails.Outputs {
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(out.Script, c.params)
		if err != nil {
			return nil, err
		}
		if out.Amount == nil {
			return nil, errors.New("nil payment amount")
		}
		output := Output{
			Address: addrs[0],
			Amount:  bchutil.Amount(int64(*out.Amount)),
		}
		pr.Outputs = append(pr.Outputs, output)
	}
	pr.Expires = time.Unix(int64(paymentDetails.GetExpires()), 0)
	if !c.skipExpirationChecks {
		if pr.Expires.Before(time.Now()) {
			return nil, errors.New("payment request is expired")
		}
	}
	pr.Memo = paymentDetails.GetMemo()
	pr.PaymentURL = paymentDetails.GetPaymentUrl()
	pr.MerchantData = paymentDetails.GetMerchantData()
	return pr, nil
}
DownloadBip0070PaymentRequest will download a Bip70 (protobuf) payment request from
the provided bitcoincash URI. Upon download it will validate the request is formatted
correctly and signed with a valid X509 certificate. The cert will be checked against
the OS's certificate store. A PaymentRequest object with the relevant data extracted
is returned.
package pymtproto
import (
	"bytes"
	"crypto/x509"
	"errors"
	"fmt"
	"github.com/gcash/bchd/chaincfg"
	"github.com/gcash/bchd/txscript"
	"github.com/gcash/bchd/wire"
	"github.com/gcash/bchutil"
	"github.com/gcash/bchwallet/pymtproto/payments"
	"github.com/golang/protobuf/proto"
	"golang.org/x/net/proxy"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"time"
)
type PaymentRequest struct {
	PayToName    string
	Outputs      []Output
	Expires      time.Time
	Memo         string
	PaymentURL   string
	MerchantData []byte
}
type Output struct {
	Address bchutil.Address
	Amount  bchutil.Amount
}
type PaymentProtocolClient struct {
	httpClient           *http.Client
	params               *chaincfg.Params
	proxyDialer          proxy.Dialer
	skipExpirationChecks bool
}
func NewPaymentProtocolClient(params *chaincfg.Params, proxyDialer proxy.Dialer) *PaymentProtocolClient {
	dial := net.Dial
	if proxyDialer != nil {
		dial = proxyDialer.Dial
	}
	tbTransport := &http.Transport{Dial: dial}
	client := &http.Client{Transport: tbTransport, Timeout: time.Minute}
	return &PaymentProtocolClient{
		httpClient:  client,
		params:      params,
		proxyDialer: proxyDialer,
	}
}