init
This commit is contained in:
194
ljcom/ssldocs/pay/cc.bml
Normal file
194
ljcom/ssldocs/pay/cc.bml
Normal file
@@ -0,0 +1,194 @@
|
||||
<?page
|
||||
title=>Process Credit Card
|
||||
body<=
|
||||
<?_code
|
||||
{
|
||||
use strict;
|
||||
use vars qw(%GET);
|
||||
|
||||
my $req = shift;
|
||||
my $r = $req->{'r'};
|
||||
|
||||
my $cart = $GET{'c'};
|
||||
my $cartobj = LJ::Pay::load_cart($cart);
|
||||
return BML::redirect("$LJ::SITEROOT/pay/") unless $cartobj;
|
||||
|
||||
# cart tied to user?
|
||||
if ($cartobj->{'userid'}) {
|
||||
|
||||
my $remote = LJ::get_remote();
|
||||
return "You must be logged in as this cart's owner to proceed with checkout."
|
||||
unless $remote;
|
||||
return "You do not own this cart. If you'd like to start a new cart, please click " .
|
||||
"<a href='./?c=new'>here</a>."
|
||||
unless $cartobj->{'userid'} == $remote->{'userid'};
|
||||
}
|
||||
|
||||
# if there is no userid, then we know it's clean because we checked
|
||||
# each individual item for email blocks when they were added to the cart
|
||||
|
||||
# is merchant gateway down?
|
||||
return "<?h1 Payment Gateway Down h1?>" .
|
||||
"<?p We're sorry, but our credit card transaction gateway is currently down. " .
|
||||
"Please try back in a few minutes. p?>"
|
||||
if $LJ::AUTHNET_DOWN;
|
||||
|
||||
my $needs_shipping = LJ::Pay::cart_needs_shipping($cartobj);
|
||||
my $has_coppa = LJ::Pay::cart_contains_coppa($cartobj);
|
||||
|
||||
my $amt_total = $cartobj->{amount};
|
||||
if ($amt_total <= 0 && $has_coppa) {
|
||||
$amt_total = '0.01';
|
||||
}
|
||||
|
||||
my $amt_charge = sprintf("%0.02f", $amt_total);
|
||||
my $amt_pretty = "\$${amt_charge} USD";
|
||||
|
||||
my $ret;
|
||||
$ret .= "<?h1 Order ($cart) h1?><?p ";
|
||||
LJ::Pay::render_cart($cartobj, \$ret, { 'secureimg' => 1 });
|
||||
$ret .= " p?>";
|
||||
|
||||
if ($cartobj->{'mailed'} ne "C") {
|
||||
$ret .= "<?h1 Already Paid h1?><?p This order has already been paid for. p?>";
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if ($needs_shipping && ! $POST{'confirm_address'}) {
|
||||
my %payvar;
|
||||
my $dbh = LJ::get_db_writer();
|
||||
my $sth = $dbh->prepare("SELECT pkey, pval FROM payvars WHERE payid=? AND pkey LIKE 'ship%'");
|
||||
$sth->execute($cartobj->{'payid'});
|
||||
while (my ($k, $v)= $sth->fetchrow_array) { $payvar{$k} = $v; }
|
||||
|
||||
my %country;
|
||||
LJ::load_codes({ "country" => \%country });
|
||||
|
||||
$ret .= "<?h1 Shipping Address h1?>";
|
||||
$ret .= "<?p If the following is incorrect, go <a href=\"$LJ::SITEROOT/pay/?c=$cart\">back to your order</a> and check out again, correcting the shipping address. p?><div style='margin-left: 50px; margin-bottom: 20px'>";
|
||||
$ret .= "$payvar{'ship_name'}<br />";
|
||||
$ret .= "$payvar{'ship_addr1'}<br />";
|
||||
$ret .= "$payvar{'ship_addr2'}<br />" if $payvar{'ship_addr2'};
|
||||
$ret .= "$payvar{'ship_city'}, $payvar{'ship_state'} $payvar{'ship_zip'}<br />";
|
||||
my $ctry = uc($payvar{'ship_country'});
|
||||
if ($ctry ne "US") {
|
||||
$ret .= $country{$ctry};
|
||||
}
|
||||
$ret .= "</div>";
|
||||
}
|
||||
|
||||
unless (LJ::Pay::is_valid_cart($cartobj)) {
|
||||
return "The given cart contains items that are no longer valid. This could be caused by conflicts with other " .
|
||||
"carts created during the same time. Please try again with a new cart.";
|
||||
}
|
||||
|
||||
# if the amount is free then it was probably free before, but needed shipping
|
||||
# so we had to get to this point before deciding whether to charge them anything
|
||||
# or not. dont' need to collect credit card information
|
||||
if ($cartobj->{'amount'} <= 0 && $needs_shipping && !$has_coppa) {
|
||||
|
||||
unless ($POST{'confirm_address'}) {
|
||||
$ret .= "<form method='post' action='cc.bml?c=$cart'>";
|
||||
$ret .= LJ::html_submit('confirm_address', "Complete Order");
|
||||
$ret .= "</form>";
|
||||
return $ret;
|
||||
}
|
||||
|
||||
# set this cart as ready to be processed, and 'free' method
|
||||
my $dbh = LJ::get_db_writer();
|
||||
$dbh->do("UPDATE payments SET used='N', mailed='N', ".
|
||||
"method='free', daterecv=NOW() WHERE payid=?", undef,
|
||||
$cartobj->{'payid'});
|
||||
|
||||
$ret .= "<?h1 Order Completed h1?>";
|
||||
$ret .= "<?p There is no charge for this order. p?>";
|
||||
return $ret;
|
||||
}
|
||||
|
||||
# tab index
|
||||
my $tidx = 0;
|
||||
$ret .= "<form method='post' autocomplete='off' action='ccpay.bml' style='display: inline'>";
|
||||
|
||||
# coppa stuff if cart contains age verification
|
||||
if ($has_coppa) {
|
||||
$ret .= "<?h1 Age Verification (for COPPA Compliance) h1?>";
|
||||
$ret .= "<?p The above request contains an age verification item for an account. In order to complete the process, we need to collect an email address for a parent of this user. p?>";
|
||||
$ret .= "<?p This email address must not be the same as the one on the account. A verification email will be sent to the address entered. It is required that you read the email and use the link inside to verify this account. p?>";
|
||||
|
||||
$ret .= "<div style='margin-left: 40px; margin-bottom: 20px;'>";
|
||||
$ret .= "<b>Parent or Guardian's Email Address:</b> ";
|
||||
$ret .= LJ::html_text({ name => 'paremail', size => '30', maxlength => '255', tabindex => ++$tidx });
|
||||
$ret .= "</div>";
|
||||
}
|
||||
|
||||
$ret .= "<?h1 Credit Card Information h1?>";
|
||||
if ($has_coppa && $cartobj->{amount} <= 0) {
|
||||
$ret .= "<?p An authorization may appear on your statement from <b>Danga Interactive</b>, ";
|
||||
$ret .= "which is the legal name of the company that runs LiveJournal.com. Some banks do ";
|
||||
$ret .= "not show authorization requests on billing statements. p?>";
|
||||
$ret .= "<?p Since your cart contains only a <a href='$LJ::SITEROOT/legal/coppa.bml'>COPPA</a> Age Verification item, ";
|
||||
$ret .= "we will attempt verify that the information you have entered is correct and that at least $amt_pretty ";
|
||||
$ret .= "is available on your card. However, you will <i>not</i> be charged. p?>";
|
||||
} else {
|
||||
$ret .= "<?p Charge will appear on your statement from <b>Danga Interactive</b>, which is the legal name of the company that runs LiveJournal.com. p?>";
|
||||
}
|
||||
$ret .= LJ::html_hidden("cart", $cart);
|
||||
$ret .= LJ::html_hidden("amt_charge", $amt_charge);
|
||||
$ret .= "<div style='margin-left: 20px'><table>";
|
||||
|
||||
$ret .= "<tr><td></td><td align='left'><b><u>Card Info</u></b></td></tr>\n";
|
||||
$ret .= "<tr><td align='right'>Name on card:</td><td>";
|
||||
$ret .= LJ::html_text({ name => 'cardname', size => '30', maxlength => '40', tabindex => ++$tidx}) . "</td></tr>";
|
||||
$ret .= "<tr><td align='right'>Card Number:</td><td>";
|
||||
$ret .= LJ::html_text({ name => 'cardnum', size => '22', maxlength => '22', tabindex => ++$tidx }) . "</td></tr>";
|
||||
$ret .= "<tr><td align='right'>Expiration Date:</td><td>";
|
||||
$ret .= LJ::html_text({ name => 'expdate_mm', size => '3', maxlength => '2', tabindex => ++$tidx }) . " / ";
|
||||
$ret .= LJ::html_text({ name => 'expdate_yy', size => '4', maxlength => '4', tabindex => ++$tidx }) . " (mm/yy)</td></tr>";
|
||||
|
||||
if ($LJ::USE_CARD_CODE) {
|
||||
$ret .= "<tr><td align='right'><a href='cvv2.bml' target='_blank'>Card Verification Number</a>:</td><td>";
|
||||
$ret .= LJ::html_text({ name => 'cardcode', size => '4', maxlength => '4', tabindex => ++$tidx }) . "</td></tr>";
|
||||
}
|
||||
|
||||
$ret .= "<tr><td></td><td align='left'><b><u>Billing Address</u></b> (for card verification)</td></tr>\n";
|
||||
$ret .= "<tr><td align='right'>Address:</td><td>";
|
||||
$ret .= LJ::html_text({ name => 'bill_addr', size => '30', maxlength => '40', tabindex => ++$tidx }) . "</td></tr>";
|
||||
$ret .= "<tr><td align='right'>City:</td><td>";
|
||||
$ret .= LJ::html_text({ name => 'bill_city', size => '30', maxlength => '40', tabindex => ++$tidx }) . "</td></tr>";
|
||||
$ret .= "<tr><td align='right'>State:</td><td>";
|
||||
$ret .= LJ::html_text({ name => 'bill_state', size => '20', maxlength => '40', tabindex => ++$tidx }) . " (code or full name)</td></tr>";
|
||||
$ret .= "<tr><td align='right'>Zip/Postal Code:</td><td>";
|
||||
$ret .= LJ::html_text({ name => 'bill_zip', size => '9', maxlength => '10', tabindex => ++$tidx }) . "</td></tr>";
|
||||
|
||||
my %countries;
|
||||
LJ::load_codes({ "country" => \%countries });
|
||||
|
||||
$ret .= "<tr><td align='right'>Country:</td><td>";
|
||||
$ret .= LJ::html_select({ name => 'bill_country', tabindex => ++$tidx },
|
||||
map { $_, $countries{$_} }
|
||||
("US", sort { $countries{$a} cmp $countries{$b} } keys %countries));
|
||||
$ret .= "</td></tr>";
|
||||
|
||||
$ret .= "<tr><td></td><td align='left'><b><u>Other Info</u></b></td></tr>\n";
|
||||
$ret .= "<tr><td align='right'>Optional Receipt Email:</td><td>";
|
||||
$ret .= LJ::html_text({ name => 'email', size => '30', maxlength => '50', tabindex => ++$tidx }) . "</td></tr>";
|
||||
$ret .= "<tr><td align='right'>Amount:</td><td><b>$amt_pretty</b></td></tr>";
|
||||
$ret .= "<tr><td align='right'></td><td>";
|
||||
if ($has_coppa && $cartobj->{amount} <= 0) {
|
||||
$ret .= LJ::html_submit(undef, 'Perform Authorization (No Charge)', { tabindex => ++$tidx });
|
||||
} else {
|
||||
$ret .= LJ::html_submit(undef, 'Make Payment', { tabindex => ++$tidx });
|
||||
}
|
||||
$ret .= "<br />(Click once and wait)</tr>";
|
||||
$ret .= "</table></div></form>";
|
||||
|
||||
$ret .= "<?h1 Security Assurance h1?><?p Your credit card info is sent over the Internet encrypted, both from your computer to LiveJournal, and from LiveJournal to the bank. Your credit card number is immediately forgotten after this transaction and never stored on any of our servers. p?>";
|
||||
return $ret;
|
||||
|
||||
}
|
||||
_code?>
|
||||
|
||||
<=body
|
||||
page?>
|
||||
|
||||
|
||||
660
ljcom/ssldocs/pay/ccpay.bml
Normal file
660
ljcom/ssldocs/pay/ccpay.bml
Normal file
@@ -0,0 +1,660 @@
|
||||
<?page
|
||||
title=>Pay with Credit Card
|
||||
body<=
|
||||
<?_code
|
||||
{
|
||||
use strict;
|
||||
use vars qw(%POST);
|
||||
use LWP;
|
||||
use LWP::UserAgent;
|
||||
|
||||
my $req = shift;
|
||||
my $r = $req->{r};
|
||||
my $uniq = $r->notes('uniq');
|
||||
|
||||
my $dbh = LJ::get_db_writer();
|
||||
my $cart = $POST{'cart'};
|
||||
my $remote = LJ::get_remote();
|
||||
my $remote_ip = LJ::get_remote_ip();
|
||||
|
||||
return BML::redirect("$LJ::SITEROOT/pay") unless LJ::did_post();
|
||||
|
||||
my $no_charge = "As a result of this error, your credit card has <i>not</i> been charged.";
|
||||
|
||||
my $error = sub {
|
||||
return LJ::bad_input($_[0], $no_charge);
|
||||
};
|
||||
|
||||
# is merchant gateway down?
|
||||
return "<?h1 Payment Gateway Down h1?>" .
|
||||
"<?p We're sorry, but our credit card transaction gateway is currently down. " .
|
||||
"Please try back in a few minutes. $no_charge p?>"
|
||||
if $LJ::AUTHNET_DOWN;
|
||||
|
||||
unless ($POST{'bill_country'} =~ /^\w+$/) {
|
||||
return $error->("Please Go back and enter your country.");
|
||||
}
|
||||
|
||||
if ($POST{'bill_country'} eq "US") {
|
||||
return $error->("Zip code required for credit card verification. Please go back and enter your zip code.")
|
||||
unless $POST{'bill_zip'} =~ /\d+/;
|
||||
}
|
||||
|
||||
my $lockname = "authnet-$cart";
|
||||
my $gotlock = $dbh->selectrow_array("SELECT GET_LOCK(?,5)", undef, $lockname);
|
||||
unless ($gotlock) {
|
||||
return $error->("Error getting lock.");
|
||||
}
|
||||
my $unlock = sub {
|
||||
$dbh->do("SELECT RELEASE_LOCK(?)", undef, $lockname);
|
||||
};
|
||||
|
||||
# make $error unlock now
|
||||
$error = sub {
|
||||
$unlock->();
|
||||
return LJ::bad_input($_[0], $no_charge);
|
||||
};
|
||||
|
||||
my $cartobj = LJ::Pay::load_cart($cart)
|
||||
or return $error->("Order not found.");
|
||||
|
||||
# do time-based rate-limiting
|
||||
if (@LJ::MEMCACHE_SERVERS) {
|
||||
|
||||
# start by checking rate keys for this cart
|
||||
my @memkeys = ([$cartobj->{payid}, "cctry_payid:$cartobj->{payid}"]);
|
||||
push @memkeys, [$cartobj->{userid}, "cctry_uid:$cartobj->{userid}"]
|
||||
if $cartobj->{userid};
|
||||
push @memkeys, [$uniq, "cctry_uniq:$uniq"]
|
||||
if $uniq;
|
||||
|
||||
# now check rate keys for all of the items, but don't note duplicate
|
||||
# email addresses/uids
|
||||
my %emails = ();
|
||||
my %userids = ();
|
||||
foreach my $it (@{$cartobj->{items}}) {
|
||||
if (my $uid = $it->{rcptid}) {
|
||||
next if $userids{$uid} || $uid == $cartobj->{userid};
|
||||
$userids{$uid}++;
|
||||
push @memkeys, [$uid, "cctry_uid:$uid"];
|
||||
} elsif (my $email = $it->{rcptemail}) {
|
||||
next if $emails{$email};
|
||||
$emails{$email}++;
|
||||
push @memkeys, [$email, "cctry_email:$email"];
|
||||
}
|
||||
}
|
||||
|
||||
my $now = time();
|
||||
my $cctry = LJ::MemCache::get_multi(@memkeys) || {};
|
||||
|
||||
my $exceeded = 0; # is the rate exceeded based on one of the time queues?
|
||||
my $sendmail = 0; # was the 10 try threshold passed? eg: send an email
|
||||
foreach my $memkey (@memkeys) {
|
||||
|
||||
my $val = $cctry->{$memkey->[1]} || [];
|
||||
my @try = grep { $_ >= $now - 3600*6 } @$val;
|
||||
my $tries = scalar @try;
|
||||
my $lasttry = $try[-1];
|
||||
|
||||
my $toofast = LJ::run_hook("ccpay_rate_check", $tries, $lasttry);
|
||||
$exceeded = 1 if $toofast;
|
||||
$sendmail = 1 if $tries == 10; # passed into "probably bot" territory
|
||||
|
||||
# save this try
|
||||
push @try, $now;
|
||||
@try = @try[-20..-1] if @try > 20;
|
||||
LJ::MemCache::set($memkey, \@try);
|
||||
}
|
||||
|
||||
# send an email if it's a moderately severe violation
|
||||
# we send an email when they go over the 10 try mark,
|
||||
# then not again since if they got this far they'll try
|
||||
# multiple times and we don't want a flood of emails
|
||||
if ($sendmail) {
|
||||
my $passtxt;
|
||||
$passtxt = "NOTE: Although this user has made 10+ tries to process their credit card, " .
|
||||
"the most recent attempt (which triggered this email) has passed rate-limit " .
|
||||
"checks. The behavior is still fishy and may require further investigation.\n\n"
|
||||
unless $exceeded;
|
||||
|
||||
LJ::send_mail({
|
||||
'to' => $LJ::ACCOUNTS_EMAIL,
|
||||
'from' => $LJ::ACCOUNTS_EMAIL,
|
||||
'wrap' => 1,
|
||||
'subject' => "CC rate limiting: payid: $cartobj->{payid}",
|
||||
'body' => "This warning has been sent because there have been at least 10 " .
|
||||
"attempts to pay for order \#$cartobj->{payid} via credit card. " .
|
||||
"No further emails will be sent about this incident since they are likely " .
|
||||
"to continue trying, but here is relevant sysban information: \n\n" .
|
||||
" Payid: $cartobj->{payid}\n" .
|
||||
" Remote User: " . ($remote ? $remote->{user} : 'unknown') . "\n" .
|
||||
" Remote IP: " . ($remote_ip || 'unknown') . "\n" .
|
||||
" Uniq Cookie: " . ($uniq || 'unknown') . "\n\n" .
|
||||
"Email addresses and userids have also been included in the rate checking. " .
|
||||
"See $LJ::SITEROOT/admin/accounts/paiddetails.bml?payid=$cartobj->{payid} " .
|
||||
"for more information.\n\n" .
|
||||
$passtxt,
|
||||
});
|
||||
}
|
||||
|
||||
# see if they're voilating rate limits
|
||||
if ($exceeded) {
|
||||
return $error->("You are attempting to process credit card transactions too frequently. " .
|
||||
"Your credit card has not been charged. Please wait a while then try again " .
|
||||
"later. If the problem persists, please contact $LJ::ACCOUNTS_EMAIL.");
|
||||
}
|
||||
}
|
||||
|
||||
# verify cart (check for items that have been invalidated since adding)
|
||||
unless (LJ::Pay::is_valid_cart($cartobj)) {
|
||||
return $error->("The given cart contains items that are no longer valid. This could be caused by conflicts with other " .
|
||||
"carts created during the same time. Please try again with a new cart.");
|
||||
}
|
||||
|
||||
# now we have a cart object
|
||||
if ($cartobj->{'userid'}) {
|
||||
|
||||
return $error->("You must be logged in as this cart's owner to proceed with checkout.")
|
||||
unless $remote;
|
||||
|
||||
return $error->("You do not own this cart.")
|
||||
unless $cartobj->{'userid'} == $remote->{'userid'};
|
||||
|
||||
if (LJ::sysban_check('pay_email', $remote->{'email'})) {
|
||||
LJ::sysban_note($remote->{'userid'},
|
||||
"Payment blocked based on email",
|
||||
{ 'email' => $remote->{'email'},
|
||||
'user' => $remote->{'user'},
|
||||
'cart' => $cart });
|
||||
|
||||
return $error->("Sorry, we were unable to process your request.");
|
||||
}
|
||||
|
||||
if (LJ::sysban_check('pay_user', $remote->{'user'})) {
|
||||
LJ::sysban_note($remote->{'userid'},
|
||||
"Payment blocked based on user",
|
||||
{ 'user' => $remote->{'user'}, 'cart' => $cart });
|
||||
|
||||
return $error->("Sorry, we were unable to process your request.");
|
||||
}
|
||||
}
|
||||
|
||||
if ($uniq && LJ::sysban_check('pay_uniq', $uniq)) {
|
||||
LJ::sysban_note($remote ? $remote->{'userid'} : 0,
|
||||
"Payment blocked based on uniq",
|
||||
{ 'user' => $remote ? $remote->{'user'} : "(anon)",
|
||||
'uniq' => $uniq,
|
||||
'cart' => $cart });
|
||||
return $error->("Sorry, we were unable to process your request.");
|
||||
}
|
||||
|
||||
# if there is no userid, then we know it's clean because we checked
|
||||
# each individual item for email blocks when they were added to the cart
|
||||
|
||||
my $has_coppa = LJ::Pay::cart_contains_coppa($cartobj);
|
||||
my $needs_shipping = LJ::Pay::cart_needs_shipping($cartobj);
|
||||
|
||||
# coppa requires remote
|
||||
if ($has_coppa && ! $remote) {
|
||||
return $error->("You must be logged in as the account being verified (the child's account) in order to process an order containing an Age Verification item.");
|
||||
}
|
||||
|
||||
my $amt_total = $cartobj->{amount};
|
||||
my $coppa_only = 0;
|
||||
if ($amt_total <= 0 && $has_coppa) {
|
||||
$amt_total = '0.01';
|
||||
$coppa_only = 1;
|
||||
}
|
||||
|
||||
return $error->("Order amount is zero.")
|
||||
unless $amt_total > 0;
|
||||
|
||||
my $u;
|
||||
if ($cartobj->{'userid'}) {
|
||||
$u = LJ::load_userid($cartobj->{'userid'});
|
||||
LJ::load_user_props($u, 'allow_pay', 'fraud_watch');
|
||||
}
|
||||
|
||||
my $country = LJ::LJcom::country_of_ip($remote_ip);
|
||||
my $now = LJ::mysql_time(time());
|
||||
LJ::Pay::payvar_add($cartobj->{'payid'}, "geoip-country", "$now: country=$country ($remote_ip)");
|
||||
|
||||
# attempt to gather and store user's country/state
|
||||
if ($POST{bill_country}) {
|
||||
my $err;
|
||||
my ($ctry, $st) = LJ::Pay::check_country_state($POST{bill_country}, $POST{bill_state}, \$err);
|
||||
return $error->($err) if $err;
|
||||
|
||||
# now save country and state in database
|
||||
LJ::Pay::payid_set_state($cartobj->{payid}, $ctry, $st);
|
||||
}
|
||||
|
||||
my $from = sub { return lc($country) eq $_[0] || lc($POST{'bill_country'}) eq $_[0] };
|
||||
|
||||
# stop Russian/Ukrainian credit card theft
|
||||
if ($from->('ru') || $from->('ua')) {
|
||||
|
||||
# see if we should force the user to be allowed to pay
|
||||
unless ($u && $u->{'allow_pay'} eq 'Y') {
|
||||
|
||||
# if cart needs shipping, don't accept any card or address from Russia/Ukraine
|
||||
return $error->("We do not currently accept credit card payments from Russia or the Ukraine on shipped goods.")
|
||||
if $needs_shipping;
|
||||
|
||||
# if cart doesn't need shipping, bill country and country must match if either is Russia/Ukraine
|
||||
return $error->("The country of your Internet address does not match the country of your credit card. From here you can:<ul>" .
|
||||
"<li>Use a different payment method or credit card.</li><li>Pay using an Internet connection in your credit card's country.</li>" .
|
||||
"<li>Contact <a href='$LJ::SITEROOT/support/'>$LJ::SITENAME support</a> and explain your situation. We may be able to help.</li></ul>")
|
||||
unless lc($country) eq lc($POST{'bill_country'});
|
||||
}
|
||||
|
||||
# otherwise we've decided to allow this user, log
|
||||
LJ::Pay::payvar_add($cartobj->{'payid'}, "allow_pay",
|
||||
"$now: cc=$POST{'bill_country'}, actual=$country, user=$u->{'user'} (allowed)");
|
||||
}
|
||||
|
||||
# throw an error if they didn't provide a parent's email
|
||||
my $paremail;
|
||||
if ($has_coppa) {
|
||||
$paremail = $POST{paremail};
|
||||
|
||||
my @email_errors;
|
||||
push @email_errors, "You must enter a parent email address to proceed with <a href='$LJ::SITEROOT/legal/coppa.bml'>COPPA</a> Age Verification."
|
||||
unless $paremail;
|
||||
push @email_errors, "The parental email address must not match your account's email address."
|
||||
if lc $remote->{email} eq lc $paremail; # ignore case changes
|
||||
|
||||
LJ::check_email($paremail, \@email_errors);
|
||||
|
||||
# throw message if necessary
|
||||
return $error->(@email_errors) if @email_errors;
|
||||
}
|
||||
|
||||
my $setup_coppa_email_validation = sub {
|
||||
# register an auth action
|
||||
my $aa = LJ::register_authaction($remote->{userid}, "parentemail", $paremail)
|
||||
or return $error->("Unable to register authaction.");
|
||||
|
||||
# now send the verification email
|
||||
LJ::send_mail({
|
||||
to => $paremail,
|
||||
from => $LJ::SUPPORT_EMAIL,
|
||||
subject => "$LJ::SITENAME Parent Email Verification",
|
||||
wrap => 1,
|
||||
body => qq{
|
||||
Welcome, you have attempted to create a $LJ::SITENAME account for your child.
|
||||
|
||||
By activating this account you certify that you have read our Children's
|
||||
Online Privacy Protection Act disclosure, Terms of Service and Privacy
|
||||
Policy and that you agree to them.
|
||||
|
||||
COPPA Disclosure
|
||||
$LJ::SITEROOT/legal/coppa.bml
|
||||
|
||||
Terms of Service
|
||||
$LJ::SITEROOT/legal/tos.bml
|
||||
|
||||
Privacy Policy
|
||||
$LJ::SITEROOT/legal/privacy.bml
|
||||
|
||||
To complete your child's journal creation and verify your email address,
|
||||
you must click on the link below.
|
||||
|
||||
$LJ::SITEROOT/agecheck/consent.bml?c=$aa->{aaid}.$aa->{authcode}
|
||||
|
||||
(If you are unable to click on the link, copy it into your browser.)
|
||||
|
||||
Your child may access his or her journal at the following two URLs:
|
||||
|
||||
$LJ::SITEROOT/users/$remote->{user}/
|
||||
$LJ::SITEROOT/~$remote->{user}/
|
||||
|
||||
Below is the $LJ::SITENAME username that was registered:
|
||||
|
||||
Username: $remote->{user}
|
||||
|
||||
If your child needs to retrieve his or her password, they can do so at any
|
||||
time by visiting the following URL:
|
||||
|
||||
$LJ::SITEROOT/lostinfo.bml
|
||||
|
||||
Enjoy!
|
||||
|
||||
$LJ::SITENAME Team
|
||||
$LJ::SITEROOT/
|
||||
},
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
my $success = sub {
|
||||
my $ret;
|
||||
|
||||
if ($coppa_only) {
|
||||
$ret .= "<?h1 Successful Authorization h1?>";
|
||||
$ret .= "<?p You have completed the first step of the <a href='$LJ::SITEROOT/legal/coppa.bml'>COPPA</a> Age Verification. Parents, please check your email: you should shortly receive a verification message. As soon as you get the email and click the link inside, your child's account will be fully active. p?>";
|
||||
$setup_coppa_email_validation->();
|
||||
|
||||
} else {
|
||||
$ret .= "<?h1 Paid h1?>";
|
||||
$ret .= "<?p This order has been successfully paid for. p?>";
|
||||
|
||||
if ($has_coppa) {
|
||||
$ret .= "<?p Additionally, you have completed the first step of the <a href='$LJ::SITEROOT/legal/coppa.bml'>COPPA</a> Age Verification. Parents, please check your email: you should shortly receive a verification email. As soon as you get the email and click the link inside, your child's account will be fully active. p?>";
|
||||
$setup_coppa_email_validation->();
|
||||
}
|
||||
}
|
||||
|
||||
if ($needs_shipping) {
|
||||
$ret .= "<?p <b>Notice:</b> Your email receipt from Authorize.net (our credit card processor) won't contain shipping information. Don't worry... that information is stored in our database. p?>";
|
||||
}
|
||||
|
||||
$ret .= "<?h1 Order ($cart) h1?><?p ";
|
||||
LJ::Pay::render_cart($cartobj, \$ret, { 'secureimg' => 1 });
|
||||
$ret .= " p?>";
|
||||
|
||||
$unlock->();
|
||||
return $ret;
|
||||
};
|
||||
|
||||
if ($cartobj->{'mailed'} ne "C") {
|
||||
# not in cart state anymore.
|
||||
# double-clicked? tell them it's all good.
|
||||
return $success->();
|
||||
}
|
||||
|
||||
my $amt_charge = sprintf("%0.02f", $amt_total);
|
||||
if ($amt_charge ne $POST{'amt_charge'}) {
|
||||
return $error->("Unexpected error: order amount changed from time you entered card info until now.");
|
||||
}
|
||||
|
||||
my $cardnum = $POST{'cardnum'};
|
||||
$cardnum =~ s![^\d]!!g; # remove non-numbers
|
||||
my $expdate = sprintf("%02d%02d", $POST{'expdate_mm'}, $POST{'expdate_yy'} % 100);
|
||||
|
||||
my $cardcode = $POST{'cardcode'};
|
||||
$cardcode =~ s!\s+!!g; # remove non-numbers
|
||||
if ($LJ::USE_CARD_CODE) {
|
||||
return $error->("You must provide your full <a href='cvv2.bml' target='_blank'>card verification number</a> for this transaction.")
|
||||
unless $cardcode =~ /^\d{3,}$/;
|
||||
}
|
||||
|
||||
# blocked credit card?
|
||||
$cardnum =~ /^(\d\d\d\d).*(\d\d\d\d)$/;
|
||||
if (LJ::sysban_check('pay_cc', "$1-$2")) {
|
||||
|
||||
my $vars = { 'cart' => $cart };
|
||||
|
||||
# no remote, there's not much useful information
|
||||
# so we'll see who they were buying for
|
||||
unless ($remote) {
|
||||
# get usernames of people they're trying to buy for
|
||||
my %rcpt_users = ();
|
||||
my $ct = 0;
|
||||
LJ::load_userids_multiple([ map { $_->{'rcptid'} => \$rcpt_users{"rcpt_user_" . ++$ct} } @{$cartobj->{'items'}} ]);
|
||||
|
||||
# fill in vars to log
|
||||
$ct = 0;
|
||||
map { $ct++, $vars->{"rcpt_email_$ct"} = $_->{'rcptemail'}
|
||||
if $_->{'rcptemail'} } @{$cartobj->{'items'}};
|
||||
$ct = 0;
|
||||
map { $ct++, $vars->{"rcpt_user_$ct"} = $rcpt_users{$_}->{'user'}
|
||||
if $rcpt_users{$_}->{'user'} } keys %rcpt_users;
|
||||
} else {
|
||||
$vars->{'email'} => $remote->{'email'};
|
||||
$vars->{'user'} => $remote->{'user'};
|
||||
}
|
||||
|
||||
LJ::sysban_note($remote ? $remote->{'userid'} : 0,
|
||||
"Payment blocked based on credit card", $vars);
|
||||
|
||||
return $error->("We are unable to process the card given.");
|
||||
}
|
||||
|
||||
# send fraud watch emails if necessary
|
||||
LJ::Pay::send_fraud_email($cartobj, $u);
|
||||
|
||||
# AUTHORIZE: see if the AVS information is accurate the the account is in good standing
|
||||
my $ua = new LWP::UserAgent;
|
||||
$ua->agent("LJ-AuthNet/1.0");
|
||||
my $vars = {
|
||||
'x_Version' => '3.1',
|
||||
'x_Delim_Data' => 'True',
|
||||
'x_Login' => $LJ::AUTHNET_USER,
|
||||
'x_Password' => $LJ::AUTHNET_PASS,
|
||||
'x_Card_Num' => $cardnum,
|
||||
'x_Exp_Date' => $expdate,
|
||||
'x_Amount' => $amt_charge,
|
||||
'x_Email' => $POST{'email'},
|
||||
'x_Email_Customer' => ($coppa_only ? 'FALSE' : 'TRUE'), # don't email receipts for coppa verifications
|
||||
'x_Customer_IP' => $remote_ip,
|
||||
'x_Cust_ID' => $cartobj->{'userid'},
|
||||
'x_Type' => 'AUTH_ONLY',
|
||||
'x_Invoice_Num' => $cart, # max 20, big enough
|
||||
'x_Address' => $POST{'bill_addr'},
|
||||
'x_City' => $POST{'bill_city'},
|
||||
'x_State' => $POST{'bill_state'},
|
||||
'x_Zip' => $POST{'bill_zip'},
|
||||
'x_Country' => $POST{'bill_country'},
|
||||
'x_Merchant_Email' => $LJ::AUTHNET_MERCHANT,
|
||||
};
|
||||
$vars->{'x_Card_Code'} = $cardcode if $LJ::USE_CARD_CODE;
|
||||
$vars->{'x_Test_Request'} = 'TRUE' if $LJ::AUTHNET_TEST;
|
||||
|
||||
my $req = new HTTP::Request POST => 'https://secure.authorize.net/gateway/transact.dll';
|
||||
$req->content_type('application/x-www-form-urlencoded');
|
||||
$req->content(join("&", map { LJ::eurl($_) . "=" . LJ::eurl($vars->{$_}) } keys %$vars));
|
||||
# Pass request to the user agent and get a response back
|
||||
my $res = $ua->request($req);
|
||||
|
||||
my ($ct, $auth_pass, $err);
|
||||
my @fields;
|
||||
if ($res->is_success) {
|
||||
$ct = $res->content;
|
||||
|
||||
# did authorization pass?
|
||||
@fields = split(/,/, $ct);
|
||||
$auth_pass = $fields[0] == 1;
|
||||
$err = $fields[3] unless $auth_pass;
|
||||
|
||||
} else {
|
||||
$err = "Could not contact payment gateway.";
|
||||
}
|
||||
|
||||
my $txn_id = $fields[6];
|
||||
|
||||
$dbh->do("INSERT INTO authnetlog (payid, cmd, datesent, ip, amt, result, response) ".
|
||||
"VALUES (?,'authonly',NOW(),?,?,?,?)", undef,
|
||||
$cartobj->{'payid'}, $remote_ip, $amt_charge, $auth_pass ? "pass" : "fail", $ct);
|
||||
|
||||
my %payvars = (
|
||||
'an-ip' => $remote_ip,
|
||||
'an-email' => $POST{'email'},
|
||||
'an-cardname' => $POST{'cardname'},
|
||||
);
|
||||
|
||||
$payvars{'an-avs'} = join('|', map { $POST{"bill_$_"} } qw(addr city state zip country));
|
||||
|
||||
my $cap_pass;
|
||||
my $cap_txn_id;
|
||||
|
||||
# CAPTURE: now it's time to get the money, if there were no AVS errors
|
||||
if ($auth_pass && ! $coppa_only) {
|
||||
my $vars = {
|
||||
'x_Version' => '3.1',
|
||||
'x_Delim_Data' => 'True',
|
||||
'x_Login' => $LJ::AUTHNET_USER,
|
||||
'x_Password' => $LJ::AUTHNET_PASS,
|
||||
'x_Trans_ID' => $txn_id,
|
||||
'x_Type' => 'PRIOR_AUTH_CAPTURE',
|
||||
};
|
||||
$vars->{'x_Test_Request'} = 'TRUE' if $LJ::AUTHNET_TEST;
|
||||
my $req = new HTTP::Request POST => 'https://secure.authorize.net/gateway/transact.dll';
|
||||
$req->content_type('application/x-www-form-urlencoded');
|
||||
$req->content(join("&", map { LJ::eurl($_) . "=" . LJ::eurl($vars->{$_}) } keys %$vars));
|
||||
my $res = $ua->request($req);
|
||||
my $ct;
|
||||
my @fields;
|
||||
if ($res->is_success) {
|
||||
$ct = $res->content;
|
||||
|
||||
# did capture pass?
|
||||
@fields = split(/,/, $ct);
|
||||
$cap_pass = $fields[0] == 1;
|
||||
$err = $fields[3] unless $cap_pass;
|
||||
|
||||
} else {
|
||||
$err = "Could not contact payment gateway.";
|
||||
}
|
||||
|
||||
$cap_txn_id = $fields[6];
|
||||
|
||||
$dbh->do("INSERT INTO authnetlog (payid, cmd, datesent, ip, amt, result, response) ".
|
||||
"VALUES (?,'priorcap',NOW(),?,?,?,?)", undef,
|
||||
$cartobj->{'payid'}, $remote_ip, $amt_charge, $cap_pass ? "pass" : "fail", $ct);
|
||||
|
||||
# keep track of trans ID & card fingerprint to enable refunds if necessary
|
||||
if ($cap_pass) {
|
||||
$cardnum =~ /^(\d\d\d\d).*(\d\d\d\d)$/;
|
||||
$payvars{'an-refund'} = join(",", $cap_txn_id, $expdate, $1, $2);
|
||||
$dbh->do("UPDATE payments SET used='N', mailed='N', ".
|
||||
"method='cc', daterecv=NOW() WHERE payid=?", undef,
|
||||
$cartobj->{'payid'});
|
||||
}
|
||||
|
||||
# VOID: if capture failed, uncapture their fund
|
||||
# 0: 2 means declined
|
||||
# 1: 2 means declined
|
||||
# 3: 27 means AVS mismat
|
||||
if ($fields[0] == 2 && $fields[1] == 2 && $fields[2] == 27 && $txn_id) {
|
||||
my $vars = {
|
||||
'x_Version' => '3.1',
|
||||
'x_Delim_Data' => 'True',
|
||||
'x_Login' => $LJ::AUTHNET_USER,
|
||||
'x_Password' => $LJ::AUTHNET_PASS,
|
||||
'x_Trans_ID' => $txn_id,
|
||||
'x_Type' => 'VOID',
|
||||
};
|
||||
$vars->{'x_Test_Request'} = 'TRUE' if $LJ::AUTHNET_TEST;
|
||||
my $req = new HTTP::Request POST => 'https://secure.authorize.net/gateway/transact.dll';
|
||||
$req->content_type('application/x-www-form-urlencoded');
|
||||
$req->content(join("&", map { LJ::eurl($_) . "=" . LJ::eurl($vars->{$_}) } keys %$vars));
|
||||
my $res = $ua->request($req);
|
||||
my $ct;
|
||||
if ($res->is_success) {
|
||||
$ct = $res->content;
|
||||
}
|
||||
my @fields = split(/,/, $ct);
|
||||
my $pass = $fields[0] == 1;
|
||||
|
||||
$dbh->do("INSERT INTO authnetlog (payid, cmd, datesent, ip, amt, result, response) ".
|
||||
"VALUES (?,'void',NOW(),?,?,?,?)", undef,
|
||||
$cartobj->{'payid'}, $remote_ip, 0.00, $pass ? "pass" : "fail", $ct);
|
||||
}
|
||||
}
|
||||
|
||||
# save payvars in db now
|
||||
foreach (keys %payvars) {
|
||||
next unless $payvars{$_};
|
||||
LJ::Pay::payvar_add($cartobj->{'payid'}, $_, $payvars{$_});
|
||||
}
|
||||
|
||||
# if a failure, note in ccfail table and possibly notify admins
|
||||
unless ($auth_pass && ($coppa_only || $cap_pass)) {
|
||||
|
||||
# note that we've failed once again
|
||||
my %done;
|
||||
my $emailtxt;
|
||||
foreach my $it ([$cartobj->{'userid'}, $cartobj->{'rcptemail'}],
|
||||
map { [ $_->{'rcptid'}, $_->{'rcptemail'} ] } @{$cartobj->{'items'}}) {
|
||||
|
||||
my $userid = $it->[0];
|
||||
my $email = $it->[1];
|
||||
my $user = $email;
|
||||
if ($userid) {
|
||||
my $u = LJ::load_userid($userid);
|
||||
$email = $u->{'email'};
|
||||
$user = $u->{'user'};
|
||||
}
|
||||
|
||||
next if $done{$email};
|
||||
$done{$email}++;
|
||||
|
||||
# get failures within the last 30 days
|
||||
my $failct = $dbh->selectrow_array("SELECT COUNT(*) FROM ccfail " .
|
||||
"WHERE email=? AND time>UNIX_TIMESTAMP()-3600*24*30",
|
||||
undef, $email);
|
||||
|
||||
# see if we've passed the threshold
|
||||
$failct++;
|
||||
if ($LJ::CCFAIL_NOTIFY && $failct % $LJ::CCFAIL_NOTIFY == 0) {
|
||||
my $now = LJ::mysql_time();
|
||||
|
||||
my $lastmailct = $failct - $LJ::CCFAIL_NOTIFY; # pretty good guess
|
||||
$emailtxt .=
|
||||
" User: " . ($user || $email) . "\n" .
|
||||
" Failures: $failct\n" .
|
||||
" Last Mail: $lastmailct failures\n" .
|
||||
" Payid: $cartobj->{'payid'}\n" .
|
||||
" Time: $now\n\n";
|
||||
|
||||
}
|
||||
|
||||
# now update table in db
|
||||
$dbh->do("REPLACE INTO ccfail (email, time, userid) " .
|
||||
"VALUES (?, UNIX_TIMESTAMP(), ?)",
|
||||
undef, $email, $userid);
|
||||
}
|
||||
|
||||
# send out the combined email now
|
||||
LJ::send_mail({
|
||||
'to' => $LJ::ACCOUNTS_EMAIL,
|
||||
'from' => $LJ::ACCOUNTS_EMAIL,
|
||||
'wrap' => 1,
|
||||
'subject' => "CC Payment Failure Notification: Payment #$cartobj->{'payid'}",
|
||||
'body' => "This warning has been sent because the following AVS failure " .
|
||||
"has occurred on $LJ::SITENAMESHORT.\n\n" .
|
||||
|
||||
" Payid: $cartobj->{'payid'}\n" .
|
||||
" Time: " . LJ::mysql_time() . "\n\n" .
|
||||
|
||||
"Below are failure stats for each user listed on this cart.\n\n" .
|
||||
|
||||
$emailtxt,
|
||||
|
||||
}) if $emailtxt;
|
||||
|
||||
}
|
||||
|
||||
if ($err) {
|
||||
$unlock->();
|
||||
return "<b>Error processing payment:</b> $err" .
|
||||
"<p>It is possible that your bank has placed a temporary hold on the funds ".
|
||||
"for this authorization, but they should be released after a short ".
|
||||
"period of time. Your card has <i>not</i> been charged.</p>" .
|
||||
|
||||
"<p>For more information on AVS and credit card transactions, " .
|
||||
"<a href='$LJ::SITEROOT/support/faqbrowse.bml?faqid=190'>" .
|
||||
"please see the FAQ</a>.</p>";
|
||||
}
|
||||
|
||||
# if the order amount is 0, mark this cart as free. this catches
|
||||
# coppa-only orders and orders containing coppa which needed to
|
||||
# continue to the cc.bml page
|
||||
if ($cartobj->{amount} <= 0) {
|
||||
$dbh->do("UPDATE payments SET used='N', mailed='N', ".
|
||||
"method='free', daterecv=NOW() WHERE payid=?", undef,
|
||||
$cartobj->{'payid'});
|
||||
}
|
||||
|
||||
# if the order was processed successfully, we'll mark the user with allow_pay
|
||||
LJ::Pay::note_payment_from_user($u);
|
||||
|
||||
# $success->() will call $unlock->() for us
|
||||
return $success->();
|
||||
}
|
||||
_code?>
|
||||
<=body
|
||||
page?>
|
||||
|
||||
|
||||
17
ljcom/ssldocs/pay/cvv2.bml
Normal file
17
ljcom/ssldocs/pay/cvv2.bml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?page
|
||||
title=>Credit Card Verification
|
||||
body<=
|
||||
<?h1 What is CVV2/CVC2/CID? h1?>
|
||||
<?p CVV2/CVC2/CID is a security feature for transactions where your credit card is not physically present. It is a three or four digit value which provides our payment system with a check of your credit card's authenticity. The terms are generally used interchangeably. CVV2 stands for "Card Verification Value 2", CVC2 stands for "Card Validation Code 2", and CID stands for "Card Identification Number". Verification numbers help us to verify two things: p?>
|
||||
<ul>
|
||||
<li>Your credit card is in your possession.</li>
|
||||
<li>Your credit card account is legitimate.</li>
|
||||
</ul>
|
||||
<?p Verification numbers are only on the card - they are not contained in the magnetic stripe information, nor do they appear on sales receipts or statements. Using the credit card verification number helps us minimize the risk of unknowingly accepting a counterfeit card or being a victim of fraud. p?>
|
||||
<?h1 Where to Find Your Verification Number h1?>
|
||||
<?p <img src="/img/visa.jpg" alt="" /><br />
|
||||
On a Visa, Eurocard/MasterCard, or Discover credit card, the verification number is the three digits to the right of the credit card number in the signature area on the back of the card. p?>
|
||||
<?p <img src="/img/amex.jpg" alt="" /><br />
|
||||
On an American Express credit card, the verification number is the four digits printed (not embossed) on the front of the card above the credit card number. p?>
|
||||
<=body
|
||||
page?>
|
||||
3
ljcom/ssldocs/pay/index.bml
Normal file
3
ljcom/ssldocs/pay/index.bml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?_code
|
||||
return BML::redirect("$LJ::SITEROOT/pay/");
|
||||
_code?>
|
||||
Reference in New Issue
Block a user