This is an old revision of the document!


Shop mit PayPal-Anbindung

FIXME translate

Der phpwcms Shop ist rudimentär, reicht aber für kleine Projekte vollkommend aus. Ärgerlich ist lediglich die beschränkte Zahlungsmöglichkeiten. PayPal wurde zwar im Backend (und teilweise im Code) vorgesehen, funktioniert jedoch bislang nicht. Mittels den folgenden Schritten gelingt es dem Shop Paypal-fähig zu machen.

Anmerkung: Es gibt dafür zwar eine alte Shopversion von nebenaube, möchte man allerdings eine modifizierte oder neuere Shopversion des phpwcms nutzen bringt dies nicht viel.

Testumgebung: Um die hier vorgestellten Möglichkeiten nutzen zu können, sollte der Shop bzw. die Bezahlung via Paypal Sandbox auf einem online erreichbaren Testserver getestet werden, da Paypal, um die Bezahlung zu verifizieren (IPN) eine URI zu eurem Shop aufruft. Diese sollte dann nicht auf den localhost führen.


Forum: http://forum.phpwcms.org/viewtopic.php?f=8&t=18505&start=15
Spezifikation: https://www.paypalobjects.com/de_DE/html/IntegrationCenter/ic_home.html
Danke an: nebenaube für PayPal Vorarbeit & OG für den Shop

Autor: Robert/ q23.media
CMS version: = ab Shop

Verzeichnis: /include/inc_module/mod_shop_paypal/

Beschreibung

#1: DB-Update

Es muß eine neuen Zwischenspeicher-Tabelle angelegt werden.

SQL-Anweisung in phpmyadmin ausführen:

CREATE TABLE  IF NOT EXISTS  `phpwcms_shop_transactions` (
  `transaction_id` int(10) unsigned NOT NULL auto_increment,
  `receiver_email` varchar(60) collate latin1_bin default NULL,
  `order_number` varchar(10) collate latin1_bin default NULL,
  `payment_status` varchar(10) collate latin1_bin default NULL,
  `pending_reason` varchar(10) collate latin1_bin default NULL,
  `payment_date` varchar(20) collate latin1_bin default NULL,
  `mc_gross` varchar(20) collate latin1_bin default NULL,
  `mc_fee` varchar(20) collate latin1_bin default NULL,
  `tax` varchar(20) collate latin1_bin default NULL,
  `mc_currency` varchar(3) collate latin1_bin default NULL,
  `txn_id` varchar(20) collate latin1_bin default NULL,
  `txn_type` varchar(10) collate latin1_bin default NULL,
  `first_name` varchar(30) collate latin1_bin default NULL,
  `last_name` varchar(40) collate latin1_bin default NULL,
  `address_street` varchar(50) collate latin1_bin default NULL,
  `address_city` varchar(30) collate latin1_bin default NULL,
  `address_state` varchar(30) collate latin1_bin default NULL,
  `address_zip` varchar(20) collate latin1_bin default NULL,
  `address_country` varchar(30) collate latin1_bin default NULL,
  `address_status` varchar(10) collate latin1_bin default NULL,
  `payer_email` varchar(60) collate latin1_bin default NULL,
  `contact_phone` varchar(50) collate latin1_bin default NULL,
  `payer_status` varchar(10) collate latin1_bin default NULL,
  `payment_type` varchar(10) collate latin1_bin default NULL,
  `notify_version` varchar(10) collate latin1_bin default NULL,
  `verify_sign` varchar(10) collate latin1_bin default NULL,
  `referrer_id` varchar(10) collate latin1_bin default NULL,
  PRIMARY KEY  (`transaction_id`)
) ENGINE=MyISAM AUTO_INCREMENT=14 ;

#2: Einspielen der notwendige Dateien

- front_order_process.inc.php
- paypal.class.php
hier herunterladen phpwcms_paypal.zip (10.39 KiB, 81 downloads) und auf den Server/ in include/inc_module/mod_shop_paypal/inc kopieren.

#3: Modifizieren des bestehenden Orderprocess

Wenn im nachfolgenden des öftern von ggf. geredet wird bedeutet das, dass die Änderung nur vorzunehmen ist sollte die Funktion/ Abfrage/ Zeile noch nicht existieren. Es wurde seitens des Autors darauf verzichtet zu prüfen ob und ab wann die (teilweise) Paypal-Integrierung bereits von OG ins System implementiert wurde.

1. Template File

Falls nicht bereits vorhanden in der default.html (bzw. diejenige Templatedatei die verwendet wird) folgende Einstellungen

Den Abschnitt:

 <--- Config_Start --->
label_payby_prepay = "Cash with order"
label_payby_pod = "Cash on delivery"
label_payby_onbill = "On account"

mit folgendem erweitern:

label_payby_paypal = "PayPal"


Im Abschnitt:

<!--MAIL_CUSTOMER_START//-->


Ausgabe der Zahlart in der eMail mit

[PAYBY_PAYPAL]You have paid via Paypal.[/PAYBY_PAYPAL]

erreichen.
Nach

<!--MAIL_NEWORDER_END//-->

und vor Order_Done_Start:

<!--PAYPAL_ORDER_DONE_START//-->
    <h2>Order {ORDER} successfully sent</h2>
    <p>Hello <strong>{INV_FIRSTNAME} {INV_NAME}</strong>,<br />
    Thanks for your order. You will receive a notification of your oder status at <strong>{EMAIL}</strong> once confirmation of your payment has been received...</p>
    <p>Your order number is: <strong>{ORDER}</strong>. Please use this number in case you need to contact us by email.</p>
<!--PAYPAL_ORDER_DONE_END//-->
 
<!--PAYPAL_ORDER_DEBUG_START//-->
    <h2>Paypal class error message</h2>
    <p><strong>{DEBUG_MSG1} </strong></p>
    <p><strong>{DEBUG_MSG2} </strong></p>
<!--PAYPAL_ORDER_DEBUG_END//-->
 
<!--PAYPAL_ORDER_CANCELED_START//-->
    <h3>{MESSAGE}</h3>
<!--PAYPAL_ORDER_CANCELED_END//-->

2: edit.preferences.inc

<?php echo $BLM['shopprod_payment_method'] ?>

suchen und ggf. danach (also wenn die neue tabelle aufgeht)

das einfügen:

            <tr>
                <td><input type="checkbox" name="pref_payment_paypal" id="pref_payment_paypal" value="1"<?php is_checked(1, $plugin['data']['shop_pref_payment']['paypal']) ?> onchange="enableSubmit();" /></td>
                <td><label for="pref_payment_paypal">&nbsp;<?php echo $BLM['shopprod_payby_paypal'] ?>&nbsp;&nbsp;&nbsp;</label></td>
                <td align="right" class="chatlist"><?php echo $BLM['shopprod_email_paypal'] ?>:&nbsp;</td>
                <td><input name="pref_email_paypal" type="text" id="pref_email_paypal" class="v12 width175" value="<?php echo html_specialchars($plugin['data']['shop_pref_email_paypal']) ?>" size="30" maxlength="200" onchange="enableSubmit();" /></td>
            </tr>

3.1: frontend.render.php

im Abschnitt 'if($_shop_load_order) {' hinter $_tmpl['mail_neworder'] das hinzufügen:

            $_tmpl['paypal_order_success']    = get_tmpl_section('PAYPAL_ORDER_DONE',     $_tmpl['source']);
            $_tmpl['paypal_order_debug']    = get_tmpl_section('PAYPAL_ORDER_DEBUG', $_tmpl['source']);
            $_tmpl['paypal_order_canceled']    = get_tmpl_section('PAYPAL_ORDER_CANCELED', $_tmpl['source']);

und im Abschnitt

// merge config settings like translations and so on
            $_tmpl['config'] = array_merge(    array(

unter mail_neworder_subject das hinzufügen:

            'label_payby_paypal' => "Paypal",

Direkt darunter unter

    // get preferences
    $_shopPref = array();

ggf. das dem foreach hinzufügen:

'shop_pref_email_paypal',

3.2: frontend.render.php

Suche bei

elseif( isset($_POST['shop_order_submit']) && !isset($_SESSION['shopping_cart']['error']['step2']) )

zusätzliche elseif abfrage danach, vor else:

} elseif( isset($_GET['shop_order_process'])  ) {
        //process order via paypal ipn
        include($phpwcms['modules']['shop']['path'].'inc/front.order_process.inc.php');

5. in frontend.render.php

Unter der stelle

    // receive order db ID
    $order_data = _dbInsert('phpwcms_shop_orders', $order_data);

ein switch einfügen:

                // success inserting db record of order
                    $_SESSION['shopping_cart']['order_number'] = $order_num;
 
                    switch( $_SESSION['shopping_cart']['payby'] ){
 
                    case 'paypal':
                        headerRedirect(PHPWCMS_URL.$_tmpl['config']['cart_url']."&shop_order_process");
                        break;
 
                    default:

wobei sich ans default die

//send mail to customer anschließt.

ACHTUNG: switch nach der

// NO success routine mit

mit

} //endswitch

wieder schließen.

6. frontend.render.php

Suche die Funktion

get_payment_options()

suchen und

$supported mit:

    $supported = array('paypal' => 0, 'prepay' => 0, 'pod' => 0, 'onbill' => 0);

überschreiben.

Anschließend

das ans ende vor dem schließenden PHP-Tag kopieren.:

function validateReferrer(  $yoursite, //Your site url without 'http://' or subdomain
                            $domain_name //Type your domain with www. this time
                         )
{
 
    $referer = $_SERVER['HTTP_REFERER'];
    //Check if browser sends referrer url or not
    if ($referer == "") { //If not, set referrer as your domain
    $domain = $yoursite;
    } else {
    $domain = parse_url($referer); //If yes, parse referrer
    }
 
    if($domain['host'] == $yoursite || $domain['host'] == $domain_name) {
 
        //Run your dowloading code here normally
 
    } else {
 
        //The referrer is not your site, we bail and redirect to  home page
        header("Location: http://".$domain_name."/index.php");
 
        exit(); //Stop running the script
 
    }
}
 
 
 
function reinitialize_session(&$_tmpl){
 
    $_SESSION['shopping_cart']['order_number'] = ( isset($_POST['invoice'])    ? clean_slweg($_POST['invoice']) : '' );
    $_SESSION['shopping_cart']['step1'] = array(
 
            'INV_FIRSTNAME'    => isset($_POST['first_name'])         ? clean_slweg($_POST['first_name']) : '',
            'INV_NAME'        => isset($_POST['last_name'])         ? clean_slweg($_POST['last_name']) : '',
            'INV_ADDRESS'    => isset($_POST['address_street'])     ? clean_slweg($_POST['address_street']) : '',
            'INV_ZIP'        => isset($_POST['address_zip'])     ? clean_slweg($_POST['address_zip']) : '',
            'INV_CITY'        => isset($_POST['address_city'])     ? clean_slweg($_POST['address_city']) : '',
            'INV_REGION'    => isset($_POST['address_state'])     ? clean_slweg($_POST['address_state']) : '',
            'INV_COUNTRY'    => isset($_POST['address_country']) ? clean_slweg($_POST['address_country']) : '',
            'EMAIL'            => isset($_POST['payer_email'])     ? clean_slweg($_POST['payer_email']) : '',
            'PHONE'            => isset($_POST['contact_phone'])     ? clean_slweg($_POST['contact_phone']) : ''
 
            );
 
 
}
 
function reinitialize_cart_products( $order_num ){
 
    $cart_data = Array();
    $cart_data[0] = "uninitialized";
 
 
    $q_result = _dbQuery("SELECT * FROM ".DB_PREPEND."phpwcms_shop_orders WHERE order_number='".$order_num."' LIMIT 1");
 
    if(isset($q_result[0])){
 
            $cart_data  = unserialize($q_result[0]['order_data']);
 
    }
 
    return $cart_data;
 
}
 
function update_transactions_table(&$cart_data, &$P){
    $temp = Array(
                "txn_id",
                "reason_code",
                "receiver_email",
                "mc_gross",
                "protection_eligibility",
                "address_status",
                "payer_id",
                "tax",
                "address_street",
                "payment_date",
                "payment_status",
                "charset",
                "address_zip",
                "mc_shipping",
                "mc_handling",
                "first_name",
                "mc_fee",
                "address_country_code",
                "address_name",
                "notify_version",
                "custom",
                "invoice",
                "num_cart_items",
                "payer_status",
                "business",
                "address_country",
                "address_city",
                "payer_email",
                "contact_phone",
                "verify_sign",
                "payment_type",
                "last_name",
                "address_state",
                "payment_fee",
                "pending_reason",
                "receiver_id",
                "txn_type",
                "mc_currency",
                "residence_country",
                "receipt_id",
                "test_ipn",
                "transaction_subject",
                "payment_gross",
                "merchant_return_link",
                "form_charset"
            );
 
 
    $trans = Array();
 
    foreach( $temp  as $key ){
 
        $trans[$key] = ( isset( $_POST[$key]) ? clean_slweg($_POST[$key]) : '');
 
    }
 
    for( $i=0; $i < $trans['num_cart_items']; $i++ ){
 
 
            $trans['item_number'.($i+1)] = ( isset( $_POST['item_number' . ($i+1)]) ? clean_slweg($_POST['item_number'. ($i+1)]) : '');
            $trans['mc_gross_'.($i+1)]     = ( isset( $_POST['mc_gross_'   . ($i+1)]) ? clean_slweg($_POST['mc_gross_'  . ($i+1)]) : '');
            $trans['quantity'.($i+1)]     = ( isset( $_POST['quantity'    . ($i+1)]) ? clean_slweg($_POST['quantity'   . ($i+1)]) : '');
 
 
    }
 
    $validTransaction = true;
 
    // test the txn_id; if txn_id is used and it's not a chargeback then it's invalid
    $txn_id_is_used = _dbCount("SELECT * FROM ".DB_PREPEND."phpwcms_shop_transactions WHERE txn_id='".$trans['txn_id']."'");
 
    if(  $txn_id_is_used && !isset($trans['reason_code']) && !( empty($trans['txn_type']) && ($trans['reason_code'] === "chargeback")) ){
        $validTransaction = false;
        $lastError  .= "Failed Anti-fraud test 1: txn_id is used: ".$txn_id_is_used."   txn_type: ".$trans['txn_type']."  trans_reason_code: ".$trans['reason_code']."\n";
    }
 
    // test receiver_email
    if( $validTransaction && !( $trans['receiver_email'] === _getConfig( 'shop_pref_email_paypal', '_shopPref' ) ) ){
        $validTransaction = false;
        $lastError  .= "Failed Anti-fraud test 2: unmatched email addresses for receiver_email: ".$trans['receiver_email']." vs "._getConfig( 'shop_pref_email_paypal', '_shopPref' )."\n";
    }
 
    // validate number of items
    if( $validTransaction && ( count($cart_data['cart']) != $trans['num_cart_items'])){
        $validTransaction = false;
        $lastError .= "Failed Anti-fraud test 3: number of items mismatch: items in cart: ".count($cart_data['cart'])." vs items in post: ".$trans['num_cart_items']."\n";
    }
 
    // test to see that cart items match
    if( $validTransaction ){
 
        for( $i=0; $i < $trans['num_cart_items']; $i++ ){
 
 
            if( $cart_data['cart'][$i]['shopprod_id']       != $trans['item_number'.($i+1)] ||
                $cart_data['cart'][$i]['shopprod_price']    != $trans['mc_gross_'.($i+1)] ||
                $cart_data['cart'][$i]['shopprod_quantity'] != $trans['quantity'.($i+1)]    ){
 
                $validTransaction = false;
 
                $lastError  .= "\n\nFailed Anti-fraud test 3: product mismatch: \n";
                $lastError  .= $cart_data['cart'][$i]['shopprod_id']." != ".$trans['item_number'.($i+1)].",\n";
                $lastError  .= $cart_data['cart'][$i]['shopprod_price']." != ".$trans['mc_gross_'.($i+1)].",\n";
                $lastError  .= $cart_data['cart'][$i]['shopprod_quantity']." != ".$trans['quantity'.($i+1)]."\n\n";
 
            }
 
        }
 
 
    }
 
 
    if( $validTransaction ){  // No fraud detected
        $data = Array(
                        'receiver_email'     => $trans['receiver_email'],
                        'order_number'        => $trans['invoice'],
                        'payment_status'    => $trans['payment_status'],
                        'pending_reason'    => $trans['pending_reason'],
                        'payment_date'        => $trans['payment_date'],
                        'mc_gross'            => $trans['mc_gross'],
                        'mc_fee'            => $trans['mc_fee'],
                        'tax'                => $trans['tax'],
                        'mc_currency'        => $trans['mc_currency'],
                        'txn_id'            => $trans['txn_id'],
                        'txn_type'            => $trans['txn_type'],
                        'first_name'        => $trans['first_name'],
                        'last_name'            => $trans['last_name'],
                        'address_street'    => $trans['address_street'],
                        'address_city'        => $trans['address_city'],
                        'address_state'        => $trans['address_state'],
                        'address_zip'         => $trans['address_zip'],
                        'address_country'      => $trans['address_country'],
                        'address_status'       => $trans['address_status'],
                        'payer_email'         => $trans['payer_email'],
                        'contact_phone'        => $trans['contact_phone'],
                        'payer_status'         => $trans['payer_status'],
                        'payment_type'         => $trans['payment_type'],
                        'notify_version'       => $trans['notify_version'],
                        'verify_sign'          => $trans['verify_sign'],
                        'referrer_id'         => $trans['referrer_id']
                    );
 
 
 
        _dbInsertOrUpdate( DB_PREPEND."phpwcms_shop_transactions",    $data, "txn_id = '".$trans['txn_id']."'", '');
 
 
    }else{
 
        $P->log_error($lastError );
    }
 
    return $validTransaction;
 
}

7. processing.preferences.inc.php

Ersetzungen wie folgt. Nach

         $plugin['data']['shop_pref_email_from']            = clean_slweg($_POST['pref_email_from']);

ggf. das einfügen

        $plugin['data']['shop_pref_email_paypal']        = clean_slweg($_POST['pref_email_paypal']);


nach

        if(! is_valid_email($plugin['data']['shop_pref_email_from']) )        $plugin['data']['shop_pref_email_from']        = '';

ggf. das einfügen:

        if(! is_valid_email($plugin['data']['shop_pref_email_paypal']) )    $plugin['data']['shop_pref_email_paypal']    = '';


vor

    'prepay' => empty($_POST['pref_payment_prepay']) ? 0 : 1,

ggf. das einfügen

    'paypal' => empty($_POST['pref_payment_paypal']) ? 0 : 1,


nach

    _setConfig('shop_pref_email_from', $plugin['data']['shop_pref_email_from'], 'module_shop');

ggf. das einfügen

    _setConfig('shop_pref_email_paypal', $plugin['data']['shop_pref_email_paypal'], 'module_shop');


nach

    'shop_pref_email_from' => '',

ggf. das einfügen

    'shop_pref_email_paypal' => '',


nach

    'prepay'=> 1,

ggf. das einfügen

    'paypal' => 1,



8. Language-Files

Ggf. die language files unter /include/inc_module/mod_shop/lang mit paypal anreichern.
Das sollte eigentlich bei phpwcms > 2007 bereits vorhanden sein.
Bspw. für deutsch nach

$BLM['shopprod_payment_method']    = 'Zahlungsmethoden';

das einfügen

$BLM['shopprod_payby_paypal']    = 'PayPal';
$BLM['shopprod_email_paypal']    = 'PayPal E-Mail';




#4: Zwischenzusammenfassung

Ab jetzt müßte

a. die Zahlart PayPal Auswahl im Shop verfügbar sein \\
b. eine Weiterleitung zu PayPal geschehen (derzeit ist noch die Sandbox Testumgebung aktiv) \\
c. der Shop innerhalb seiner normalen Parameter funktionieren. \\


Sollte eine der oben aufgeführten Punkte nicht zutreffen gibt es ein Problem. Kontrollier bitte noch einmal ganz genau die Punkt eins bis drei.

Sollte es zu eigenartigen Weiterleitungen beim ABschliessen des Bestellprozesses kommen, dann trifft dies zu: Die zur Verfügung gestellte Datei front.order_process.inc.php enthält einen Abschitt, zum Überprüfen des Referrer.

    switch ($_GET['action']) {

        case 'process':      // Process and order...
            validateReferrer(   $phpwcms['site_domain'],
                        $phpwcms['machine_alias'].'.'.$phpwcms['site_domain']
                            );//or bail...

Diese Parameter $phpwcms['machine_alias'] und $phpwcms['site_domain'] existieren Standardmäßig nicht und sollten ggf. in die config.inc.php nachgetragen werden. Konkret wäre das:

$phpwcms['site_domain'] = 'domain.de'; // ohne www oder subdomain
$phpwcms['machine_alias'] = 'alias'; // www oder subdomain

Bitte ggf auch die frontend.render.php ⇒ function validateReferrer() einsehen und verstehen was da passiert!

#5.1: Übersetzung

Wird ein Kunde zu Paypal weiter geleitet erscheint eine Meldung. Diese ist derzeit in englischer Sprache. Unter paypal.class.php kann dies verändert werden.

Such nach

// Generate the request header

und ersetze das darauf folgende

beispielhaft mit:

    echo "<html>\n";
    echo "<head><title>PayPal Zahlung...</title></head>\n";
 
    echo "<body onLoad=\"document.forms['paypal_form'].submit();\">\n";
 
    echo "<center><h2>Bitte warten. Sie werden jetzt zu PayPal weiter geleitet.</h2></center>\n";
    echo "<form method=\"post\" name=\"paypal_form\" ";
    echo "action=\"".$this->paypal_url."\">\n";
 
    foreach ($this->fields as $name => $value) {
        echo "<input type=\"hidden\" name=\"$name\" value=\"$value\"/>\n";
    }
    echo "<center><br/><br/>Sollten Sie nicht innerhalb der nächsten Sekunden weitergeleitet werden ";
    echo "klicken Sie bitte hier...<br/><br/>\n";
    echo "<input type=\"submit\" value=\"Zu PayPal\"></center>\n";
 
    echo "</form>\n";
    echo "</body></html>\n";



#5.2: Übersetzung

Die Success/ Error Page muß ebenso angepasst werden. Diese -wie zuvor angelegt/ eingefügt- im Template ganz am Ende zu finden.

beispielhafte deutsche übersetzung:

<!--PAYPAL_ORDER_DONE_START//-->
    <h2>Bestellung  #{ORDER} erfolgreich platziert</h2>
    <p>Hallo <strong>{INV_FIRSTNAME} {INV_NAME}</strong>,<br />
    Vielen Dank für deine Bestellung. Du erhälst eine Benachrichtigung an <strong>{EMAIL}</strong> sobald uns Deine Bezahlung von PayPal bestätigt wurde.</p>
    <p>Deine Bestellnummer lautet: <strong>{ORDER}</strong>. Bitte verwende die Nummer bei Rückfragen.</p>
<!--PAYPAL_ORDER_DONE_END//-->
 
<!--PAYPAL_ORDER_DEBUG_START//-->
    <h2>Paypal Fehler</h2>
    <p><strong>{DEBUG_MSG1} </strong></p>
    <p><strong>{DEBUG_MSG2} </strong></p>
<!--PAYPAL_ORDER_DEBUG_END//-->




#6: Beheben des Steuerbug

Zwar gibt es unter Shop > Produkte die Möglichkeit zu bestimmen ob ein Preis in brutto oder netto vorliegt. Die Erweiterung wertet dies jedoch nicht aus sondern geht standardmäßig von einem Netto-Preis aus. Das bedeutet PayPal rechnet noch einmal die Mehrwertsteuer oben drauf. Diese Erweiterung beseitigt das unerwünschte Verhalten.

ACHTUNG: Hier wäre eine Abfrage wünschenswert die anhand der Erfassung im Backend entscheidet ob Artikel mit oder ohne MwSt. an Paypal übergeben werden muß. Der folgende Core-Hack erfordert alle Produkte des Shops als Brutto.

Vorgehensweise: Quick&Dirty wird die Tax-Zeile aus der Übergabe an PayPal entfernt.

Suche in paypal.class.php:

      foreach ($this->fields as $name => $value) {
        echo "<input type=\"hidden\" name=\"$name\" value=\"$value\"/>\n";
      }

Ersetze die Funktion mit:

      foreach ($this->fields as $name => $value) {
        //remove tax from data-output  + q23.media 23102011
        if (substr($name, 0, 3) != 'tax') {
                 echo "<input type=\"hidden\" name=\"$name\" value=\"$value\"/>\n";
        }
      }
deutsch/module/shop/paypal.1324412553.txt.gz · Last modified: 2018/06/03 18:07 (external edit)
www.planmatrix.de www.chimeric.de Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0