# -*-cperl-*-
#
# Business::Bitcoin - Easy and secure way to accept Bitcoin payments online
# Copyright (c) 2016-2017 Ashish Gulhati <biz-btc at hash dot neomailbox.ch>
#
# $Id: lib/Business/Bitcoin.pm v1.036 Sun Apr  2 10:12:41 PDT 2017 $

use strict;

package Business::Bitcoin;

use 5.010000;
use warnings;
use strict;

use DBI;
use Business::Bitcoin::Request;

use vars qw( $VERSION $AUTOLOAD );

our ( $VERSION ) = '$Revision: 1.036 $' =~ /\s+([\d\.]+)/;

sub new {
  my $class = shift;
  my %args = @_;
  return undef unless $args{XPUB};
  return undef if $args{StartIndex} and $args{StartIndex} =~ /\D/;
  unlink $args{DB} if $args{Clobber} and $args{DB} ne ':memory:';
  my $db = DBI->connect("dbi:SQLite:dbname=$args{DB}", undef, undef, {AutoCommit => 1});
  my @tables = $db->tables('%','%','requests','TABLE');
  unless ($tables[0]) {
    if ($args{Create}) {
      return undef unless $db->do('CREATE TABLE requests (
		                                         reqid INTEGER PRIMARY KEY AUTOINCREMENT,
		                                         amount int NOT NULL,
                                                         address text,
                                                         refid text UNIQUE,
		                                         created int NOT NULL,
                                                         processed int,
                                                         status text
		                                        );');
      return undef unless $db->do('CREATE INDEX idx_requests_address ON requests(address);');
      return undef unless $db->do('CREATE INDEX idx_requests_refid ON requests(refid);');
      my $startindex = $args{StartIndex} || 0;
      $startindex--;
      return unless $db->do("INSERT INTO SQLITE_SEQUENCE values ('requests',$startindex);");
    }
    else {
      return undef;
    }
  }
  bless { XPUB => $args{XPUB}, DB => $db }, $class;
}

sub request {                      # Create a Bitcoin payment request
  my ($self, %args) = @_;
  return undef if $args{Amount} !~ /^\d+$/;

  # Workaround for SQLite not starting sequence from 0 when asked to in new()
  my $startindex = $self->db->selectcol_arrayref("SELECT seq from SQLITE_SEQUENCE WHERE name='requests';")->[0];
  my %forcezero; %forcezero = ( StartIndex => 0 ) if $startindex == -1;

  my $req = new Business::Bitcoin::Request (_BizBTC => $self, %args, %forcezero);
}

sub findreq {                      # Retrieve a previously created request
  my ($self, %args) = @_;
  my $req = _find Business::Bitcoin::Request (_BizBTC => $self, %args);
}

sub AUTOLOAD {
  my $self = shift; (my $auto = $AUTOLOAD) =~ s/.*:://;
  return if $auto eq 'DESTROY';
  if ($auto =~ /^(xpub|db)$/x) {
    $self->{"\U$auto"} = shift if (defined $_[0]);
  }
  if ($auto =~ /^(xpub|db|version)$/x) {  
    return $self->{"\U$auto"};
  }
  else {
    die "Could not AUTOLOAD method $auto.";
  }
}

1; # End of Business::Bitcoin

__END__

=head1 NAME

Business::Bitcoin - Easy and secure way to accept Bitcoin payments online

=head1 VERSION

 $Revision: 1.036 $
 $Date: Sun Apr  2 10:12:41 PDT 2017 $

=head1 SYNOPSIS

An easy and secure way to accept Bitcoin payments online using an HD
wallet, generating new receiving addresses on demand and keeping the
wallet private key offline.

    use Business::Bitcoin;

    my $bizbtc = new Business::Bitcoin (DB => '/tmp/bizbtc.db',
                                        XPUB => 'xpub...',
                                        Create => 1);

    my $req = $bizbtc->request(Amount => 4200);

    print 'Please pay '           . $req->amount . ' Satoshi ' .
          'to Bitcoin address '   . $req->address . ".\n" .
          'Once the payment has ' . $req->confirmations , ' confirmations, ' .
          "press <enter> to continue.\n";
    readline(*STDIN);

    print ($req->verify ? "Verified\n" : "Verification failed\n");

    print "Enter a request address to verify a payment.\n";
    my $address = <STDIN>; chomp $address;

    my $req2 = $bizbtc->findreq(Address => $address);
    print ($req2->verify ? "Verified\n" : "Verification failed\n");

=head1 HOW TO USE

To start receiving Bitcoin payments online, create an HD wallet using
a BIP-32 HD wallet app such as Electrum, get the "Master Public Key"
for the wallet (a string beginning with "xpub") and plug it into the
constructor's XPUB argument.

Now you can receive online payments as outlined above, while keeping
your private key secure offline. You should still take all precautions
to ensure that your XPUB key on the server is also safe, as its
compromise can weaken your security, though it can't in itself lead to
the loss of any Bitcoin.

Currently the key derivation path used in this module is m/0/i which
corresponds to what Electrum uses. More flexibility in selecting the
derivation path and presets for paths used by other wallets will be
implemented in future versions.

=head1 METHODS

=head2 new

Create a new Business::Bitcoin object and open (or create) the
requests database. The following named arguments are required:

=over

DB - The filename of the requests database

XPUB - The master public key for the wallet receiving payments

=back

The following optional named arguments can be provided:

=over

Create - Create the requests table if it doesn't exist. If the table
doesn't exist and Create is not true, the constructor will return
undef. Unset by default.

Clobber - Wipe out any existing database file first. Unset by default.

StartIndex - Start generating receiving keys from the specified index
rather than from 0. Useful if you've already used some receiving
addresses before starting to receive payments using this
module. Only relevant when Create is true and a new
requests table is being created. Ignored when an existing
requests table is being used; in that case the index is generated by the
database. By default, receiving addresses will be generated starting
from the first one, at index 0.

=back

=head2 request

Create a new payment request and generate a new receiving
address. Returns a Business::Bitcoin::Request object if successful, or
undef on error. The following named argument is required:

=over

Amount - The amount of the payment requested, in Satoshi.

=back

The following optional named arguments can be provided:

=over

Confirmations - The number of confirmations needed to verify payment
of this request. The default is 5.

Reference - Optional reference ID to be associated with the request,
to facilitate integration with existing ordering systems. If a
reference ID is provided it should be unique for each request.

=back

=head2 findreq

Find a previously created payment request, by either Address or
Reference. Returns a Business::Bitcoin::Request object if successful,
or undef on error. Exactly one of the following named arguments is
required:

=over

Address - The receiving address associated with the payment request

Reference - The reference ID associated with the payment request

=back

The following optional named argument can be provided:

=over

Confirmations - The number of confirmations needed to verify payment
of this request. The default is 5.

=back

=head1 ACCESSORS

Accessors can be called with no arguments to query the value of an
object property, or with a single argument, to set the property to a
specific value (unless the property is read only).

=head2 db

The filename of the requests DB file.

=head2 xpub

The master public key from which all receiving keys are generated.

=head2 version

The version number of this module. Read only.

=head1 PREREQUISITES

=head2 DBD::SQLite

Used to keep track of payment requests.

=head2 LWP and an Internet connection

Required to verify payments. Currently this is done via the
blockchain.info API.

=head1 AUTHOR

Ashish Gulhati, C<< <biz-btc at hash dot neomailbox.ch> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-business-bitcoin at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Business-Bitcoin>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.

=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Business::Bitcoin

You can also look for information at:

=over 4

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Business-Bitcoin>

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/Business-Bitcoin>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/Business-Bitcoin>

=item * Search CPAN

L<http://search.cpan.org/dist/Business-Bitcoin/>

=back

=head1 LICENSE AND COPYRIGHT

Copyright (c) 2016-2017 Ashish Gulhati.

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.
