package Web::MarketReceipt::Verifier::AppStore;
use 5.010;
use Mouse;
extends 'Web::MarketReceipt::Verifier';
use utf8;
use Carp;
no Mouse;
use Web::MarketReceipt;
use Furl;
use JSON::XS;
use Try::Tiny;
use MIME::Base64;
sub verify {
my ($self, %args) = @_;
my $environment = 'Production';
my $receipt = $args{receipt};
my $password = $args{password};
my $exclude_old_transactions = $args{exclude_old_transactions};
my $hash = {
'receipt-data' => $receipt,
};
if ($password) {
$hash->{password} = $password;
}
if ($exclude_old_transactions) {
$hash->{'exclude-old-transactions'} = \1;
}
# try production environment first. cf.
# https://developer.apple.com/library/ios/technotes/tn2259/_index.html FAQ16
# http://nantekottai.com/2012/08/27/verifying-receipts/
my $res_json = $self->_send_request(
environment => $environment,
hash => $hash,
opts => $args{opts},
);
if ($res_json->{status} == 21007) {
$environment = 'Sandbox';
$res_json = $self->_send_request(
environment => $environment,
hash => $hash,
);
}
my $raw_json = $res_json->{receipt};
Web::MarketReceipt->new(
is_success => $res_json->{status} == 0 ? 1 : 0,
store => 'AppStore',
raw => $raw_json,
$res_json->{status} == 0 ? (
exists $raw_json->{in_app} ? (
orders => [ map {$self->_order2hash($_, $environment)} @{ $raw_json->{in_app}}],
) : (
orders => [$self->_order2hash($raw_json, $environment)],
),
) : (),
);
}
sub _order2hash {
my ($self, $raw_json, $environment) = @_;
return {
product_identifier => $raw_json->{product_id},
unique_identifier => 'AppStore:' . $raw_json->{original_transaction_id},
purchased_epoch => $raw_json->{original_purchase_date_ms},
quantity => $raw_json->{quantity},
environment => $environment,
state => 'purchased',
}
}
sub _send_request {
my ($self, %args) = @_;
my $environment = $args{environment};
my $url = {
Production => 'https://buy.itunes.apple.com/verifyReceipt',
Sandbox => 'https://sandbox.itunes.apple.com/verifyReceipt',
}->{$environment};
state $json_driver = JSON::XS->new->utf8;
my $json_str = $json_driver->encode($args{hash});
state $furl = Furl->new(%{ $args{opts} });
my $res = $furl->post($url, [], $json_str);
if ($res->status != 200) {
Web::MarketReceipt::Verifier::AppStore::Exception->throw(
message => sprintf('HTTP Status Error(%s):%s %s', $environment, $res->status, $res->message),
response => $res,
environment => $environment,
);
}
my $res_json = eval { $json_driver->decode($res->body) } or Web::MarketReceipt::Verifier::AppStore::Exception->throw(
message => sprintf('body is not json(%s)', $environment),
response => $res,
environment => $environment,
);
$res_json;
}
package Web::MarketReceipt::Verifier::AppStore::Exception;
use strict;
use warnings;
use utf8;
use parent 'Exception::Tiny';
use Class::Accessor::Lite (
ro => [qw/response environment/]
);
sub throw {
my ($class, %args) = @_;
($args{package}, $args{file}, $args{line}) = caller(2);
$args{subroutine} = (caller(3))[3];
die $class->new(%args);
}
1;