package Payment; # # SPDX-License-Identifier: AGPL-3.0-or-later # Copyright (c) Rainer Gümpelein, TeilRad GmbH # #Adapted from Prelogic Rechnung "buchen" and "print_pdf" #Adapted from payone_post.pl # #enable for syntax check #use lib "/var/www/copri-bike/shareedms-primary/src"; use strict; use warnings; use POSIX; use CGI; # only for debugging use LWP::UserAgent; use URI::Encode; my $uri_encode = URI::Encode->new( { encode_reserved => 1 } ); use Scalar::Util qw(looks_like_number); use Lib::Config; use Mod::Callib; use Mod::DBtank; use Mod::Basework; use Data::Dumper; my $q = new CGI; my $cf = new Config; my $cal = new Callib; my $dbt = new DBtank; my $bw = new Basework; sub new { my $class = shift; my $self = {}; bless($self,$class); return $self; } #book_payment is like payone_capture with additional payment-types sub book_payment { my $self = shift; my $q = shift; my $varenv = shift; my $node_meta = shift; my $users_dms = shift; $q->import_names('R'); my $dbh = ""; my $feedb = { u_rows => 0, message => "", exit_code => 1, }; my $now_dt = strftime "%Y-%m-%d %H:%M:%S", localtime; open(EMA, ">> $varenv->{logdir}/book_payment.log"); print EMA "\n*** $now_dt invoice pdf print c_id4trans:$R::c_id4trans\n" . Dumper($q) . "\n"; my $pref_ctt = { table => "contenttrans", fetch => "one", c_id => $R::c_id4trans, }; my $ctt = { c_id => 0 }; $ctt = $dbt->fetch_record($dbh,$pref_ctt) if($R::c_id4trans); my $update_ctt = { table => "contenttrans", mtime => "now()", owner => $users_dms->{u_id}, }; my $node_faktura = $dbt->get_node($dbh,$dbt->{shareedms_conf}->{faktura}); #invoice number counter. Take last number from node.invoice_nr and increment it if($node_faktura->{invoice_nr} > 0 && $ctt->{c_id} && $ctt->{ct_name} !~ /\d/){ my $nextNr = $node_faktura->{invoice_nr}; $update_ctt->{ct_name} = "$nextNr"; $update_ctt->{barcode} = "$nextNr"; my $update_node = { table => "nodes", main_id => "$dbt->{shareedms_conf}->{faktura}", change => "no_time", }; my $invoice_nr = $node_faktura->{invoice_nr} + 1; $dbt->update_one($dbh,$update_node,"invoice_nr='$invoice_nr'"); } print EMA "Used invoice c_id:$ctt->{c_id} with invoice nr:$ctt->{ct_name} OR nextNr: $update_ctt->{ct_name}\n"; #Set sum values and book payment depending by selected payment-type "state" if($ctt->{c_id} && !$ctt->{close_time}){ my $pref_adr = { table => "contentadr", fetch => "one", c_id => $ctt->{int10}, }; my $ctadr = { c_id => 0 }; $ctadr = $dbt->fetch_tablerecord($dbh,$pref_adr) if($ctt->{int10}); print EMA "Used adr c_id:$ctadr->{c_id} by ctt.int10: $ctt->{int10}\n"; my $sum_paid = "null"; my $sum_operatorcredit = "null"; my $sumgeb_teil = "null"; my $sumgeb_bank = "null"; my $state = $R::state || ""; $update_ctt->{state} = "$state"; $update_ctt->{int14} = 2;#set OPOS if($R::sum_paid){ $sum_paid = $R::sum_paid; $sum_paid =~ s/,/\./; $update_ctt->{int01} = $sum_paid; } if($R::sum_operatorcredit){ $sum_operatorcredit = $R::sum_operatorcredit; $sum_operatorcredit =~ s/,/\./; $update_ctt->{int02} = $sum_operatorcredit; $update_ctt->{int14} = "null"; } if($R::sumgeb_teil){ $sumgeb_teil = $R::sumgeb_teil; $sumgeb_teil =~ s/,/\./; $update_ctt->{int08} = $sumgeb_teil; } if($R::sumgeb_bank){ $sumgeb_bank = $R::sumgeb_bank; $sumgeb_bank =~ s/,/\./; $update_ctt->{int07} = $sumgeb_bank; } $feedb->{u_rows} = $dbt->update_record($dbh,$update_ctt,$ctt); #$R::state alias payment-type if($R::state =~ /payone/){ if(!$ctt->{txt16}){ #preauth if($ctadr->{ct_name} =~ /\w{2}-\d+/){ my $payoneret = $self->preauthorizationSEPA_main($varenv,$ctadr,$ctt,$users_dms->{u_id}); sleep 2; }elsif(length($ctadr->{ct_name}) >= 19){ my $payoneret = $self->preauthorizationCC_main($varenv,$ctadr,$ctt,$users_dms->{u_id}); sleep 2; } $ctt = $dbt->fetch_record($dbh,$pref_ctt);#re-read values #SEPA capture if($ctt->{txt16} && $R::state =~ /SEPA/){#SEPA my $payoneret = $self->captureSEPA_main($varenv,$ctadr,$ctt,$users_dms->{u_id}); } #CC capture elsif($ctt->{txt16} && $R::state =~ /Kreditkarte/){#CC my $payoneret = $self->captureCC_main($varenv,$ctadr,$ctt,$users_dms->{u_id}); } else{ $feedb->{message} = "failure::Achtung, die payone Vorautorisierung hat keine TXID geliefert. Der Geldeinzug war somit nicht möglich (TXID:$ctt->{txt16} && $R::state)."; } }else{ $feedb->{message} = "failure::Abbruch, payone Geldeinzug nicht ausgeführt weil TXID bereits vorhanden. Hatte der Einzug bereits stattgefunden?"; } } elsif($ctt->{txt00} eq "Storno" || $state eq "Zahlungsausfall"){ $update_ctt->{int14} = "null"; $update_ctt->{pay_time} = "now()"; $feedb->{u_rows} = $dbt->update_record($dbh,$update_ctt,$ctt); #delete OPOS at all $update_ctt->{barcode} = $ctt->{barcode}; if($update_ctt->{barcode}){ $dbt->update_one($dbh,$update_ctt,"int14=null"); }else{ $feedb->{message} = "failure::Fehler, OPOS Automatik konnte nicht ausgeführt werden."; } } elsif($state eq "fehlgeschlagener Einzug"){ $update_ctt->{int14} = "null"; $update_ctt->{pay_time} = "now()"; $feedb->{u_rows} = $dbt->update_record($dbh,$update_ctt,$ctt); } elsif($ctt->{txt00} eq "Rechnung" && $R::sum_paid <= 0){ $update_ctt->{int14} = "null"; $update_ctt->{pay_time} = "now()"; $feedb->{u_rows} = $dbt->update_record($dbh,$update_ctt,$ctt); } $ctt = $dbt->fetch_record($dbh,$pref_ctt);#re-read values #print pdf and messaging if($ctt->{c_id}){ my $praefix = "$ctt->{txt00}-$varenv->{praefix}"; my $lang_ctt = $ctt->{txt11} || "de"; $varenv->{cms} = $dbt->fetch_cms($dbh,{ lang => $lang_ctt }); my $sum_paid = $R::sum_paid || 0; $sum_paid =~ s/,/\./; #print pdf if($R::print_pdf){ $feedb = $self->print_pdf($q,$varenv,$node_meta,$users_dms,$feedb); } #send_invoice after book payment if(-f "$varenv->{basedir}/pdfinvoice/$praefix-$ctt->{ct_name}.pdf" && (($R::set_state eq "buchen" && $R::send_invoice && $ctt->{int01} && $ctt->{int01} != 0) || ($R::send_invoice_again))){ my $cms_message_key = "email-invoice"; if(!$varenv->{cms}->{$cms_message_key}->{txt}){ $feedb->{message} = "failure::Achtung, CMS-Text '$cms_message_key' ist nicht vorhanden. Es wurde keine eMail versandt!"; }elsif($sum_paid != $ctt->{int01}){ $feedb->{message} = "failure::Achtung, die Summe der Positionen $sum_paid enstpricht nicht der Rechnung-Summe $ctt->{int01}. Die Rechnung muss vor dem eMail versand erst gebucht werden!"; }else{ system("$dbt->{copri_conf}->{basedir}/$varenv->{syshost}/src/scripts/mailTransportcms.pl '$varenv->{syshost}' 'send_invoice' '$ctt->{int10}' '$ctt->{c_id}' '' '$cms_message_key' '1'"); print EMA "---> send_invoice $praefix-$ctt->{ct_name}.pdf email command: $dbt->{copri_conf}->{basedir}/$varenv->{syshost}/src/scripts/mailTransportcms.pl '$varenv->{syshost}' 'send_invoice' '$ctt->{int10}' '$ctt->{c_id}' '' '$cms_message_key' '1'\n"; } } }#end print pdf and messaging }else{ $feedb->{message} = "failure::Buchung abbgebrochen. Die Rechnung ist bereits abgeschlossen oder nicht vorhanden ($ctt->{c_id} > 0 && $ctt->{ct_name} && !$ctt->{close_time})"; } close EMA; return $feedb; }#end book_payment #print pdf sub print_pdf { my $self = shift; my $q = shift; my $varenv = shift; my $node_meta = shift; my $users_dms = shift; my $feedb = shift; $q->import_names('R'); my $dbh = ""; my $pref_ctt = { table => "contenttrans", fetch => "one", c_id => $R::c_id4trans, }; my $ctt = { c_id => 0 }; $ctt = $dbt->fetch_record($dbh,$pref_ctt) if($R::c_id4trans); open(EMA, ">> $varenv->{logdir}/copri-print.log"); if($ctt->{c_id}){ my $api_file = "/var/www/copri4/shareeconf/apikeys.cfg"; my $aconf = Config::General->new($api_file); my %apikeyconf = $aconf->getall; my $mandant_id = 100002; my $print_return = ""; my $lang_ctt = $ctt->{txt11} || "de"; my $praefix = "$ctt->{txt00}-$varenv->{praefix}"; my $psize="A4"; my $topdf = "$varenv->{basedir}/src/wkhtmltopdf-amd64"; #without system() because we have to wait until PDF is ready $print_return = `$topdf --page-size $psize "$varenv->{wwwhost}/Printpreview?printer_id=PDF\&mandant_main_id=$mandant_id\&main_id=$node_meta->{main_id}\&ct_name2print=$ctt->{ct_name}\&c_id4trans=$ctt->{c_id}\&u_id=$users_dms->{u_id}\&pkey=$apikeyconf{pdfprinter}->{pkey}" "$varenv->{pdf}/$praefix-$ctt->{ct_name}.pdf" 2>&1`; $feedb->{exit_code} = $?; my $filesize = -s "$varenv->{pdf}/$praefix-$ctt->{ct_name}.pdf"; print EMA "$topdf --page-size $psize $varenv->{wwwhost}/Printpreview?printer_id=PDF\&mandant_main_id=$mandant_id\&main_id=$node_meta->{main_id}\&ct_name2print=$ctt->{ct_name}\&c_id4trans=$ctt->{c_id}\&u_id=$users_dms->{u_id}\&pkey=$apikeyconf{pdfprinter}->{pkey} $varenv->{pdf}/$praefix-$ctt->{ct_name}.pdf\nreturn: $print_return\nfilesize: $filesize\nexit_code: $feedb->{exit_code}\n\n"; if($R::print_pdfview){ if( -f "$varenv->{basedir}/pdf/$praefix-$ctt->{ct_name}.pdf"){ print ""; }else{ $feedb->{message} = "failure::PDF konnte nicht generiert werden, bitte Info an: admin\@sharee.bike\n $varenv->{wwwhost}/pdf/$praefix-$ctt->{ct_name}.pdf"; } } } close EMA; return $feedb; }#end print_pdf #send_invoice_cms for variable ticket-mailing sub send_invoice_cms { my $self = shift; my $q = shift; my $varenv = shift; my $node_meta = shift; my $users_dms = shift; my $feedb = shift; $q->import_names('R'); my $dbh = ""; my $pref_ctt = { table => "contenttrans", fetch => "one", c_id => $R::c_id4trans, }; my $ctt = { c_id => 0 }; $ctt = $dbt->fetch_record($dbh,$pref_ctt) if($R::c_id4trans); if($ctt->{c_id}){ my $praefix = "$ctt->{txt00}-$varenv->{praefix}"; my $lang_ctt = $ctt->{txt11} || "de"; $varenv->{cms} = $dbt->fetch_cms($dbh,{ lang => $lang_ctt }); my $sum_paid = $R::sum_paid || 0; $sum_paid =~ s/,/\./; #print pdf if($R::print_pdf){ $feedb = $self->print_pdf($q,$varenv,$node_meta,$users_dms,$feedb); } my $cms_message_key = $R::cms_message_key; if(!$varenv->{cms}->{$cms_message_key}->{txt}){ $feedb->{message} = "failure::Achtung, CMS-Text '$cms_message_key' ist nicht vorhanden. Es wurde keine eMail versandt!"; }if($sum_paid != $ctt->{int01}){ $feedb->{message} = "failure::Achtung, die Summe der Positionen $sum_paid enstpricht nicht der Rechnung-Summe $ctt->{int01}. Die Rechnung muss vor dem eMail versand erst gebucht werden!"; }else{ my $with_pdf = ""; $with_pdf = 1 if(-f "$varenv->{basedir}/pdfinvoice/$praefix-$ctt->{ct_name}.pdf" && $R::print_pdf); system("$dbt->{copri_conf}->{basedir}/$varenv->{syshost}/src/scripts/mailTransportcms.pl '$varenv->{syshost}' 'send_invoice_cms' '$ctt->{int10}' '$ctt->{c_id}' '' '$cms_message_key' '$with_pdf'"); } } return $feedb; }#end send_invoice_cms #Tagesabschluss sub close_transactions { my $self = shift; my $q = shift; my $varenv = shift; my $node_meta = shift; my $users_dms = shift; $q->import_names('R'); my $dbh = ""; my $feedb = { u_rows => 0, message => "", }; #could be Kunden-Faktura 100002 my $adr_close = { c_id => 3, txt01 => '', txt02 => '', txt03 => '', txt06 => '', txt07 => '', txt08 => '', txt10 => '', txt11 => '', }; my $now_dt = strftime "%Y-%m-%d %H:%M:%S", localtime; open(EMA, ">> $varenv->{logdir}/close_transactions.log"); print EMA "\n*** $now_dt close_transactions\n" . Dumper($q) . "\n"; my $journal_id = "300011"; my $journal_tpl = "209"; my $ct_id = $dbt->insert_contenttrans($dbh,$adr_close,$journal_id,$journal_tpl,"",$users_dms->{u_id}); my $pref = { table => "contenttrans", fetch => "one", main_id => $journal_id, template_id => $journal_tpl, c_id => $ct_id, }; my $ctt = { c_id => 0 }; $ctt = $dbt->fetch_record($dbh,$pref) if($pref->{c_id}); if($ctt->{c_id}){ my $pref_close = { table => "contenttrans", close_id => $ctt->{c_id}, main_id => $journal_id, template_id => $journal_tpl, source_main_id => '300008,300009,300011',#Rechnung,Storno,Verkaufsjournal source_template_id => 218, }; $feedb->{u_rows} += $dbt->update_close_transactions($dbh,$pref_close); } close EMA; return $feedb; }#end close_transactions #SEPA request "managemandate" sub managemandate_main { my $self = shift; my $varenv = shift; my $ctadr = shift; my $ctt = shift || ""; my $owner = shift || 0; my $payoneret = ""; my $payone_conf = $dbt->{payone_conf} || {}; if($ctadr->{c_id}){ my $lastname = $ctadr->{txt01}; (my $firstname,$lastname) = split(/\s+/,$ctadr->{txt01}) if($ctadr->{txt01} =~ /\w\s+\w/i); chomp($firstname); chomp($lastname); my $city = $ctadr->{txt06}; (my $zip, $city) = split(/\s+/,$ctadr->{txt06}) if($ctadr->{txt06} =~ /[\w\d]\s+[\w\d]/i); chomp($zip); chomp($city); $ctadr->{txt06} =~ s/[\d\s]+//g; $ctadr->{txt22} =~ s/\s//g; my $bcountry = uc($1) if($ctadr->{txt22} && $ctadr->{txt22} =~ /^(\w{2})/); my $currency = "EUR"; #$currency = "CHF" if($bcountry eq "CH"); $ctadr->{txt23} =~ s/\s//g; my $preauth_request = { request => 'managemandate', clearingtype => 'elv', salution => "$ctadr->{txt02}", firstname => "$firstname", lastname => "$lastname", street => "$ctadr->{txt03}", zip => "$zip", city => "$city", country => "$ctadr->{txt10}", email => "$ctadr->{txt08}", telephonenumber => "$ctadr->{txt07}", currency => "$currency", iban => uc($ctadr->{txt22}), bic => uc($ctadr->{txt23}) }; my $request = { %$payone_conf, %$preauth_request}; $payoneret = $self->rpc("managemandate",$varenv,$request,$ctadr,$ctt,$owner) if($request); } return $payoneret; }#end SEPA request "managemandate" #Request "preauthorizationSEPA" sub preauthorizationSEPA_main { my $self = shift; my $varenv = shift; my $ctadr = shift; my $ctt_rec = shift; my $owner = shift || 0; my $payoneret = ""; my $payone_conf = $dbt->{operator}->{$varenv->{dbname}}->{payone_conf} || $dbt->{payone_conf}; my $dbh = ""; #to get actual data my $pref = { table => "contenttrans", fetch => "one", template_id => 218, c_id => $ctt_rec->{c_id}, }; my $ctt = { c_id => 0 }; $ctt = $dbt->fetch_record($dbh,$pref); if($ctt_rec->{payone_reset}){ if($ctt->{ct_name} =~ /\d+-\d+/){ my ($ct_name,$subname) = split(/-/,$ctt->{ct_name}); $subname++; $ctt->{ct_name} = "$ct_name-$subname"; }else{ $ctt->{ct_name} = "$ctt->{ct_name}-1"; } } my $preauth_amount = 0; my $reference = ""; #for testing payment-data if($ctt_rec->{c_id} && $ctt_rec->{c_id} == 1 && $ctt_rec->{reference}){ $ctt = $ctt_rec; $preauth_amount = $ctt->{int15};#int15 should only used for testing payment-data $reference = $ctt_rec->{reference}; }else{ $preauth_amount = $ctt->{int01}; $reference = $dbt->{operator}->{$varenv->{dbname}}->{oprefix} . "-S-" . $ctt->{ct_name}; } #if reference still set then count if($ctt->{txt25} && $ctt_rec->{payone_reset}){ if($ctt->{txt25} =~ /\d-\d$/){ my ($refbase,$sub) = split(/-/,$ctt->{txt25}); $sub++; $reference = "$refbase-$sub"; }else{ $reference = "$ctt->{txt25}-1"; } } if($ctadr->{c_id} && $ctt->{c_id} && $preauth_amount > 0){ my $lastname = $ctadr->{txt01}; (my $firstname,$lastname) = split(/\s+/,$ctadr->{txt01}) if($ctadr->{txt01} =~ /\w\s+\w/i); chomp($firstname); chomp($lastname); my $city = $ctadr->{txt06}; (my $zip, $city) = split(/\s+/,$ctadr->{txt06}) if($ctadr->{txt06} =~ /[\w\d]\s+[\w\d]/i); chomp($zip); chomp($city); $ctadr->{txt22} =~ s/\s//g; #my $bcountry = uc($1) if($ctadr->{txt22} && $ctadr->{txt22} =~ /^(\w{2})/); my $currency = "EUR"; #$currency = "CHF" if($bcountry eq "CH"); $ctadr->{txt23} =~ s/\s//g; my $amount = 0; $amount = $preauth_amount * 100 if($preauth_amount); my $preauth_request = { request => 'preauthorization', clearingtype => 'elv', salution => "$ctadr->{txt02}", firstname => "$firstname", lastname => "$lastname", street => "$ctadr->{txt03}", zip => "$zip", city => "$city", country => "$ctadr->{txt10}", email => "$ctadr->{txt08}", telephonenumber => "$ctadr->{txt07}", amount => "$amount", currency => "$currency", iban => uc($ctadr->{txt22}), bic => uc($ctadr->{txt23}), reference => "$reference" }; $preauth_request->{ip} = "$ctadr->{txt25}" if($ctadr->{txt25}); my $request = { %$payone_conf, %$preauth_request}; $payoneret = $self->rpc("preauthorizationSEPA",$varenv,$request,$ctadr,$ctt,$owner) if($request); } return $payoneret; }#end Request "preauthorizationSEPA" #Request "captureSEPA" sub captureSEPA_main { my $self = shift; my $varenv = shift; my $ctadr = shift; my $ctt_rec = shift; my $owner = shift || 0; my $payoneret = ""; my $payone_conf = $dbt->{operator}->{$varenv->{dbname}}->{payone_conf} || $dbt->{payone_conf}; my $dbh = ""; #to get actual data my $pref = { table => "contenttrans", fetch => "one", #template_id => 218, c_id => $ctt_rec->{c_id}, }; my $ctt = { c_id => 0 }; $ctt = $dbt->fetch_record($dbh,$pref); my $TXID = $ctt->{txt16} || ""; $TXID = $ctt_rec->{txid} if($ctt_rec->{txid}); my $sequence = 1; $sequence = $ctt_rec->{sequence} if($ctt_rec->{sequence}); if($ctt->{c_id} && (!$ctt->{state} || $ctt->{int14} || $ctt_rec->{payone_reset})){ my $amount = 0;#if payone_reset capture 0 $amount = $ctt->{int01} * 100 if(looks_like_number($ctt->{int01}) && !$ctt_rec->{payone_reset}); my $preauth_request = { request => 'capture', amount => "$amount", currency => "EUR", txid => "$TXID", sequencenumber => "$sequence" }; my $request = { %$payone_conf, %$preauth_request}; $payoneret = $self->rpc("captureSEPA",$varenv,$request,$ctadr,$ctt,$owner) if($request); } return $payoneret; }#end Request "captureSEPA" #CC #Request "preauthorizationCC" sub preauthorizationCC_main { my $self = shift; my $varenv = shift; my $ctadr = shift; my $ctt_rec = shift; my $owner = shift || 0; my $payoneret = ""; my $payone_conf = $dbt->{operator}->{$varenv->{dbname}}->{payone_conf} || $dbt->{payone_conf}; my $dbh = ""; #to get actual data my $pref = { table => "contenttrans", fetch => "one", template_id => 218, c_id => $ctt_rec->{c_id}, }; my $ctt = { c_id => 0 }; $ctt = $dbt->fetch_record($dbh,$pref); if($ctt_rec->{payone_reset}){ if($ctt->{ct_name} =~ /\d+-\d+/){ my ($ct_name,$subname) = split(/-/,$ctt->{ct_name}); $subname++; $ctt->{ct_name} = "$ct_name-$subname"; }else{ $ctt->{ct_name} = "$ctt->{ct_name}-1"; } } my $preauth_amount = 0; my $reference = ""; #for testing payment-data if($ctt_rec->{c_id} && $ctt_rec->{c_id} == 1 && $ctt_rec->{reference}){ $ctt = $ctt_rec; $preauth_amount = $ctt->{int15};#int15 should only used for testing payment-data $reference = $ctt_rec->{reference}; }else{ $preauth_amount = $ctt->{int01}; $reference = $dbt->{operator}->{$varenv->{dbname}}->{oprefix} . "-C-" . $ctt->{ct_name}; } #if reference still set then count if($ctt->{txt25} && $ctt_rec->{payone_reset}){ if($ctt->{txt25} =~ /\d-\d$/){ my ($refbase,$sub) = split(/-/,$ctt->{txt25}); $sub++; $reference = "$refbase-$sub"; }else{ $reference = "$ctt->{txt25}-1"; } } if($ctadr->{c_id} && $ctt->{c_id} && $preauth_amount > 0){ my $lastname = $ctadr->{txt01}; (my $firstname,$lastname) = split(/\s+/,$ctadr->{txt01}) if($ctadr->{txt01} =~ /\w\s+\w/); chomp($firstname); chomp($lastname); my $city = $ctadr->{txt06}; (my $zip, $city) = split(/\s+/,$ctadr->{txt06}) if($ctadr->{txt06} =~ /[\w\d]\s+[\w\d]/); chomp($zip); chomp($city); my $amount = 0; $amount = $preauth_amount * 100 if($preauth_amount); my $preauth_request = { request => 'preauthorization', clearingtype => 'cc', salution => "$ctadr->{txt02}", firstname => "$firstname", lastname => "$lastname", street => "$ctadr->{txt03}", zip => "$zip", city => "$city", country => "$ctadr->{txt10}", email => "$ctadr->{txt08}", telephonenumber => "$ctadr->{txt07}", amount => "$amount", currency => 'EUR', pseudocardpan => "$ctadr->{ct_name}", ecommercemode => "internet", # wird zu 3Dscheck, reference => "$reference" }; # https://docs.payone.com/display/public/PLATFORM/Special+remarks+-+Recurring+transactions+credit+card # https://docs.payone.com/display/public/INT/Best+Practices+for+PSD2#tab-3DS+2.0+Best+Case $preauth_request->{ip} = "$ctadr->{txt25}" if($ctadr->{txt25}); my $request = { %$payone_conf, %$preauth_request}; $payoneret = $self->rpc("preauthorizationCC",$varenv,$request,$ctadr,$ctt,$owner) if($request); } return $payoneret; }#end Request "preauthorizationCC" #Request "captureCC" sub captureCC_main { my $self = shift; my $varenv = shift; my $ctadr = shift; my $ctt_rec = shift; my $owner = shift || 0; my $payoneret = ""; my $payone_conf = $dbt->{operator}->{$varenv->{dbname}}->{payone_conf} || $dbt->{payone_conf}; my $dbh = ""; #to get actual data my $pref = { table => "contenttrans", fetch => "one", #template_id => 218, c_id => $ctt_rec->{c_id}, }; my $ctt = { c_id => 0 }; $ctt = $dbt->fetch_record($dbh,$pref); my $TXID = $ctt->{txt16}; $TXID = $ctt_rec->{txid} if($ctt_rec->{txid}); my $sequence = 1; $sequence = $ctt_rec->{sequence} if($ctt_rec->{sequence}); if($ctt->{c_id} && (!$ctt->{state} || $ctt->{int14} || $ctt_rec->{payone_reset})){ my $amount = 0;#if payone_reset capture 0 $amount = $ctt->{int01} * 100 if(looks_like_number($ctt->{int01}) && !$ctt_rec->{payone_reset}); my $preauth_request = { request => 'capture', amount => "$amount", currency => 'EUR', txid => "$TXID", sequencenumber => "$sequence" }; my $request = { %$payone_conf, %$preauth_request}; $payoneret = $self->rpc("captureCC",$varenv,$request,$ctadr,$ctt,$owner) if($request); } return $payoneret; }#end Request "captureCC" #TODO #with previous preauthorization/ authorization and clearingtype=”elv”: #An “amount = 0” can be used to cancel a #direct debit transaction. This is not possible if the parameter “due_time” has #been used, if the portal has enabled a delayed settlement (setup by PAYONE) or #the direct debit has already been processed (after midnight). #./src/scripts/payone_post.pl $varenv{syshost} refund contenttrans "" 6799 4 ##Request "refund" (Rückerstattung) #txt16=txid must be copied from last captured invoice. #int01 sum must be set! #sequenz = 2 sub refund { my $self = shift; my $varenv = shift; my $ctadr = shift; my $ctt = shift; my $owner = shift || 0; my $sequenz = shift || 0; my $payoneret = ""; my $payone_conf = $dbt->{operator}->{$varenv->{dbname}}->{payone_conf} || $dbt->{payone_conf}; if($ctt->{c_id}){ my $amount = 0; $amount = $ctt->{int01} * 100 if($ctt->{int01}); my $currency = "EUR"; my $preauth_request = { request => 'refund', sequencenumber => "$sequenz",#$sequenz= must be +1 of the last capture amount => "$amount", currency => "$currency", txid => "$ctt->{txt16}" }; my $request = { %$payone_conf, %$preauth_request}; $payoneret = $self->rpc("refund",$varenv,$request,$ctadr,$ctt,$owner) if($request); } return $payoneret; } #################################################################################### #Create a request sub rpc { my $self = shift; my $todo = shift; my $varenv = shift; my $request = shift; my $ctadr = shift || { c_id => 0 }; my $ctt = shift || { c_id => 0 }; my $owner = shift || 0; my $payoneret = ""; my $dbh = ""; my $ua = LWP::UserAgent->new( ssl_opts => { SSL_version => 'TLSv12:!SSLv2:!SSLv3:!TLSv1:!TLSv11', } ); $ua->agent("sharee payone POST API"); #payone API URL my $payoneLive = 1; my $httpReqServer = "https://api.pay1.de/post-gateway/"; my $req = HTTP::Request->new(POST => "$httpReqServer"); my $post; foreach (keys (%$request)){ my $encoded_val = $uri_encode->encode($request->{$_}); $post .= "$_=$encoded_val&"; } $post =~ s/\&$//; $req->content_type('application/x-www-form-urlencoded'); $req->content($post); #Pass request to the user agent and get a response back my $res = $ua->request($req); my $vde_on_fail = $ctadr->{int12} || 1;#keep last or set 1 my $debug=0; $debug=1; my $update_adr = { table => "contentadr", #mtime => "now()", #owner => $owner }; my $update_ctt = { table => "contenttrans", mtime => "now()", owner => $owner }; my $now_dt = strftime "%Y-%m-%d %H:%M:%S", localtime; open(FILE,">>$varenv->{logdir}/payone-return-post.log") if($debug); print FILE "\n*** $now_dt (ctadr_id:$ctadr->{c_id}, ctt_id:$ctt->{c_id}) from Payment.pm\n$httpReqServer \n" if($debug); print FILE "---> request to payone $todo:\n$post\n"; #Check the outcome of the response if ($res->is_success) { print FILE "<--- return from payone $todo:\n" . $res->content . "\n" if($debug); #print FILE Dumper($res); my @content = split(/\n/,$res->content); print FILE $res->status_line, "\n" if($debug); if($res->content =~ /status=APPROVED|status=REDIRECT/){ #SEPA if($todo =~ /managemandate/){ my $mival = ""; $mival = $1 if($res->content =~ /mandate_identification=(.*)/); $payoneret = $mival; print FILE "payone MANDATE $now_dt\n mival: $mival && $ctadr->{c_id}\n" if($debug); if($mival && $ctadr->{c_id}){ foreach(@content){ my ($key,$val) = split(/=/,$_); $val = $q->escapeHTML("$val"); $update_adr->{txt22} = $val if($key eq "iban"); $update_adr->{txt23} = $val if($key eq "bic"); $update_adr->{ct_name} = $val if($key eq "mandate_identification"); $update_adr->{txt27} = $val if($key eq "mandate_status"); #$update_adr->{txt28} = $val if($key eq "mandate_text" && ($val =~ /SEPA/ || !$val)); $update_adr->{txt28} = $now_dt . " $todo\n" . $q->escapeHTML($res->content); } $update_adr->{int12} = 0 if($vde_on_fail != 2);#Vde $dbt->update_record($dbh,$update_adr,$ctadr) if($ctadr->{c_id} > 0); my $ret = $self->pdfmandat($varenv,$ctadr->{c_id}); print FILE "pdfmandat call generates: $ret\n" if($debug); }elsif($ctadr->{c_id}){ $update_adr->{int12} = $vde_on_fail;#Vde } print FILE "managemandate update_adr:" . Dumper($update_adr) . "\n" if($debug); } my $txidval = ""; #CC and SEPA after preauthorization if($todo =~ /preauthorization/){ $txidval = $1 if($res->content =~ /txid=(\d+)/); $payoneret = $txidval; print FILE "payone PREAUTH $now_dt\n $todo: $txidval && $ctt->{c_id} && $ctadr->{c_id}\n" if($debug); my $useridval = $1 if($res->content =~ /userid=(\d+)/);#2020-02-11 preauthorization returns payone Debitorennr $update_ctt->{ct_name} = $ctt->{ct_name} if($ctt->{ct_name}); if($txidval && $ctt->{c_id} && $ctadr->{c_id}){ $update_ctt->{int17} = $useridval if($useridval); $update_ctt->{txt16} = $txidval; $update_ctt->{txt22} = $ctt->{payone_reset} if($ctt->{payone_reset}); $update_ctt->{txt26} = $ctadr->{ct_name};#Mandat/pseudocp $update_adr->{int12} = 0; $update_adr->{int17} = $useridval if($useridval); }elsif($ctadr->{c_id}){ $update_ctt->{int14} = 1;#OPOS $update_adr->{int12} = $vde_on_fail;#Vde } #2022-12-15 save log for any $update_ctt->{txt28} = $now_dt . " $todo\n" . $res->content . "\n" . $update_ctt->{txt28}; } #Capture if($todo =~ /capture/){ $txidval = $1 if($res->content =~ /txid=(\d+)/); $payoneret = $txidval; print FILE "payone CAPTURE $now_dt\n $todo: txid=$txidval && ctt.c_id=$ctt->{c_id} && ctadr.c_id=$ctadr->{c_id}\n" if($debug); if($txidval && $ctt->{c_id} && $ctadr->{c_id} && $res->content =~ /settleaccount=/){ #int01 and state will be set by "buchen" via Prelogic or via payone_cron/Payment payone_capture $update_ctt->{int14} = "null"; $update_adr->{int12} = 0; }else{#because of Prelogic logic set it empty if no capture $update_ctt->{int14} = 1;#OPOS $update_adr->{int12} = $vde_on_fail;#Vde #TOD send_capture_fail mail? } $update_ctt->{txt28} = $now_dt . " $todo\n" . $res->content . "\n" . $update_ctt->{txt28}; } }else{#not APPROVED print FILE "NOT APPROVED $now_dt\n $todo: ctt.c_id=$ctt->{c_id} && ctadr.c_id=$ctadr->{c_id}" . $res->content . "\n" if($debug); $update_ctt->{int14} = 1 if($ctt->{state});#OPOS #errormessage=Reference number already exists --> disabled #errormessage=Amount no longer available --> disabled if($res->content !~ /errorcode=911/){ if($payoneLive == 1 && $ctadr->{c_id}){ $update_ctt->{txt28} = $now_dt . " $todo\n" . $res->content . "\nVde.\n" . $update_ctt->{txt28}; $update_adr->{txt28} = $now_dt . " $todo\n" . $res->content . "\nVde.\n" . $update_adr->{txt28}; #never delete on state=occupied, in this case ist must delete it on available if($res->content !~ /errorcode=80/){ $update_adr->{int12} = $vde_on_fail;#Vde } } }else{ if($payoneLive == 1 && $ctt->{c_id}){ $update_ctt->{txt28} = $now_dt . " $todo\n" . $res->content . "\n" . $update_ctt->{txt28}; $update_adr->{txt28} = $now_dt . " $todo\n" . $res->content . "\n" . $update_adr->{txt28}; } } } }else { print FILE $res->status_line, "\n" if($debug); } print FILE "payone RPC end\n\n" if($debug); close(FILE) if($debug); #set contentadr owner and mtime only if vde or payone-return will be set by payone id's if((($update_adr->{int12} && $update_adr->{int12} > 0) || $update_adr->{txt28}) && ($owner == 178 || $owner == 179)){ $update_adr->{owner} = "$owner"; $update_adr->{mtime} = "now()"; } if($ctadr->{c_id} > 0){ $dbt->update_record($dbh,$update_adr,$ctadr); #2023-04-11 set it global by update adr also on primary #disabled, because isuser_rentable will be only used by operator rental #my $dbh_primary = $dbt->dbconnect($dbt->{primary}->{sharee_primary}->{database}->{dbname}); #$dbt->update_record($dbh_primary,$update_adr,$ctadr); } $dbt->update_record($dbh,$update_ctt,$ctt) if($ctt->{c_id} > 0); return $payoneret; } #SEPA PDFGenerator sub pdfmandat { my $self = shift; my $varenv = shift; my $c_id = shift || 0; my $api_file = "/var/www/copri4/shareeconf/apikeys.cfg"; my $aconf = Config::General->new($api_file); my %apikeyconf = $aconf->getall; my $dbh = ""; my $authref = { table => "contentadr", fetch => "one", template_id => "202", c_id => "$c_id", }; my $ctadr = { c_id => 0 }; $ctadr = $dbt->fetch_record($dbh,$authref) if($c_id); my $now_dt = strftime "%Y-%m-%d %H:%M:%S", localtime; open(EMA, ">> $varenv->{logdir}/copri-print.log"); print EMA "*** $now_dt trying pdf --> $varenv->{basedir}/pdfinvoice/SEPA-Lastschriftmandat-$varenv->{dbname}-$ctadr->{ct_name}.pdf && $ctadr->{txt27}\n"; if($ctadr->{txt27} && $ctadr->{txt27} =~ /active|pending/){ my $topdf = "$varenv->{basedir}/src/wkhtmltopdf-amd64"; my $print_return = `$topdf --page-size A4 "$varenv->{wwwhost}/PDFGenerator?printer_id=SEPA-Lastschriftmandat\&mandant_main_id=$dbt->{shareedms_conf}->{parent_id}\&id=$ctadr->{c_id}\&pkey=$apikeyconf{pdfprinter}->{pkey}" $varenv->{basedir}/pdfinvoice/SEPA-Lastschriftmandat-$varenv->{dbname}-$ctadr->{ct_name}.pdf 2>&1`; my $exit_code = $?; my $filesize = -s "$varenv->{basedir}/pdfinvoice/SEPA-Lastschriftmandat-$varenv->{dbname}-$ctadr->{ct_name}.pdf"; print EMA "$topdf --page-size A4 '$varenv->{wwwhost}/PDFGenerator?printer_id=SEPA-Lastschriftmandat\&mandant_main_id=$dbt->{shareedms_conf}->{parent_id}\&id=$ctadr->{c_id}\&pkey=$apikeyconf{pdfprinter}->{pkey}' $varenv->{basedir}/pdfinvoice/SEPA-Lastschriftmandat-$varenv->{dbname}-$ctadr->{ct_name}.pdf\nreturn: $print_return\nfilesize: $filesize\nexit_code: $exit_code\n"; } close EMA; return "$varenv->{basedir}/pdfinvoice/SEPA-Lastschriftmandat-$varenv->{dbname}-$ctadr->{ct_name}.pdf"; }#end SEPA PDFGenerator #jused by payone_cron.pl sub payone_capture { my $self = shift; my $varenv = shift; my $ctf = shift; my $ctadr = shift; my $ctt = shift; my $sum_paid = shift; my $owner = shift; my $lang = "de"; my $mandant_id = 100002; my $main_id = $ctt->{main_id}; my $retval = ""; my $return_text = ""; my $dbh = ""; my $api_file = "/var/www/copri4/shareeconf/apikeys.cfg"; my $aconf = Config::General->new($api_file); my %apikeyconf = $aconf->getall; my $update_ctt = { table => "contenttrans", mtime => "now()", owner => $owner, int01 => $sum_paid, int14 => 2, }; my $node_faktura = $dbt->get_node($dbh,$dbt->{shareedms_conf}->{faktura}); if($node_faktura->{invoice_nr} > 0){ if($ctt->{ct_name} !~ /\d/){ my $nextNr = $node_faktura->{invoice_nr}; $update_ctt->{ct_name} = "$nextNr"; $update_ctt->{barcode} = "$nextNr"; my $update_node = { table => "nodes", main_id => "$dbt->{shareedms_conf}->{faktura}", change => "no_time", }; my $invoice_nr = $node_faktura->{invoice_nr} + 1; $dbt->update_one($dbh,$update_node,"invoice_nr='$invoice_nr'"); } }else{ $return_text = "payone_cron Payment.pm exit, $node_faktura->{invoice_nr} | $ctt->{ct_name} can not generate invoice number\n"; return $return_text; } my $state = $ctt->{state}; if($dbt->{shareedms_conf}->{payment_state}){ my @_paymentstate = split(/\|/,$dbt->{shareedms_conf}->{payment_state}); if($ctadr->{ct_name} =~ /\w{2}-\d+/){ $state = "$_paymentstate[0]"; }else{ undef $_paymentstate[0]; } if(length($ctadr->{ct_name}) >= 19){ $state = "$_paymentstate[1]"; }else{ undef $_paymentstate[1]; } } $update_ctt->{state} = "$state"; $dbt->update_record($dbh,$update_ctt,$ctt); #in cron we set OPOS anyway. If payone captured, it will be set int14=null if(!$state || $state !~ /payone/){ $return_text = "Payment.pm can not preauthorization because of absent payment-data in ctadr.c_id:$ctadr->{c_id}, SEPA/CC:$ctadr->{int03}, $ctadr->{ct_name}, we exit\n"; return $return_text; } #preauth if($ctadr->{ct_name} && $ctadr->{ct_name} =~ /\w{2}-\d+/ && !$ctt->{txt16}){ $self->preauthorizationSEPA_main($varenv,$ctadr,$ctt,$owner); sleep 2; }elsif($ctadr->{ct_name} && length($ctadr->{ct_name}) >= 19 && !$ctt->{txt16}){ $self->preauthorizationCC_main($varenv,$ctadr,$ctt,$owner); sleep 2; } #check if preauth txid is done by payone my $pref = { table => "contenttrans", fetch => "one", template_id => 218, c_id => $ctt->{c_id}, }; $ctt = $dbt->fetch_record($dbh,$pref); #SEPA capture if($ctadr->{ct_name} =~ /\w{2}-\d+/ && $ctt->{txt16} && $ctt->{state} =~ /SEPA/){#SEPA $self->captureSEPA_main($varenv,$ctadr,$ctt,$owner); } #CC capture elsif(length($ctadr->{ct_name}) >= 19 && $ctt->{txt16} && $ctt->{state} =~ /Kreditkarte/){#CC $self->captureCC_main($varenv,$ctadr,$ctt,$owner); } else{ $return_text = "Payment.pm can not get TXID ($ctadr->{int03} && $ctadr->{ct_name} && TXID:$ctt->{txt16})\n"; } #wkhtml if(1==1){ my $praefix = "$ctt->{txt00}-$varenv->{praefix}";#like Rechnung-sharee_operator my $topdf = "$varenv->{basedir}/src/wkhtmltopdf-amd64"; my $exit_code = 1; my $print_return = ""; $print_return = `$topdf --page-size A4 "$varenv->{wwwhost}/Printpreview?printer_id=PDF\&mandant_main_id=$mandant_id\&main_id=$main_id\&ct_name2print=$ctt->{ct_name}\&c_id4trans=$ctt->{c_id}\&u_id=$owner\&pkey=$apikeyconf{pdfprinter}->{pkey}" "$varenv->{pdf}/$praefix-$ctt->{ct_name}.pdf" 2>&1`; $exit_code = $?; sleep 2; my $now_dt = strftime "%Y-%m-%d %H:%M:%S", localtime; my $filesize = -s "$varenv->{pdf}/$praefix-$ctt->{ct_name}.pdf"; open(EMA, ">> $varenv->{logdir}/copri-print.log"); print EMA "\n$now_dt\n$topdf --page-size A4 \"$varenv->{wwwhost}/Printpreview?printer_id=PDF\&mandant_main_id=$mandant_id\&main_id=$main_id\&ct_name2print=$ctt->{ct_name}\&c_id4trans=$ctt->{c_id}\&u_id=$owner\&pkey=$apikeyconf{pdfprinter}->{pkey}\" $varenv->{pdf}/$praefix-$ctt->{ct_name}.pdf\nreturn: $print_return\nfilesize: $filesize\nexit_code: $exit_code\n"; #send_invoice infomail, only if eMail never sent if(-f "$dbt->{copri_conf}->{basedir}/$varenv->{syshost}/pdfinvoice/$praefix-$ctt->{ct_name}.pdf" && !$ctt->{txt30}){ system("$dbt->{copri_conf}->{basedir}/$varenv->{syshost}/src/scripts/mailTransportcms.pl '$varenv->{syshost}' 'send_invoice' '$ctt->{int10}' '$ctt->{c_id}' '' 'email-invoice' '1'"); print EMA "---> Payment send_invoice $praefix-$ctt->{ct_name}.pdf email command: $dbt->{copri_conf}->{basedir}/$varenv->{syshost}/src/scripts/mailTransportcms.pl '$varenv->{syshost}' 'send_invoice' '$ctt->{int10}' '$ctt->{c_id}' '' 'email-invoice' '1'\n"; } close EMA; } return ($retval,$return_text); } 1;