/** * Perform verification of the payment, this is called from the payment gatewa * * @return bool Whether the payment is valid */ public function verify_payment() { $this->registry->input->clean_array_gpc('p', array('serial-number' => vB_Cleaner::TYPE_NOHTML)); if (!$this->registry->GPC['serial-number']) { $this->sendHeader(false); $this->error_code = 'missing_serial_number'; return false; } if (!$this->test()) { $this->sendHeader(false); $this->error_code = 'Payment processor not configured'; return false; } $xml = new vB_XML_Builder(); $xml->add_group('notification-history-request', array('xmlns' => 'http://checkout.google.com/schema/2')); $xml->add_tag('serial-number', $this->registry->GPC['serial-number']); $xml->close_group('notification-history-request'); $xmlString = $xml->fetch_xml(); $submitUrl = ($this->settings['sandbox'] ? $this->sandboxNotifyUrl : $this->productionNotifyUrl) . trim($this->settings['google_merchant_id']); $headers = array('Authorization: Basic ' . base64_encode(trim($this->settings['google_merchant_id']) . ':' . trim($this->settings['google_merchant_key'])), 'Content-Type: application/xml; charset=UTF-8', 'Accept: application/xml; charset=UTF-8'); $vurl = new vB_vURL(); $vurl->set_option(VURL_URL, $submitUrl); $vurl->set_option(VURL_USERAGENT, 'vBulletin/' . SIMPLE_VERSION); $vurl->set_option(VURL_HTTPHEADER, $headers); $vurl->set_option(VURL_POST, 1); $vurl->set_option(VURL_POSTFIELDS, $xmlString); $vurl->set_option(VURL_RETURNTRANSFER, 1); $vurl->set_option(VURL_CLOSECONNECTION, 1); $result = $vurl->exec(); $xmlobj = new vB_XML_Parser($result); $xmlobj->include_first_tag = true; $parsed_xml = $xmlobj->parse(); if ($parsed_xml === false or !is_array($parsed_xml)) { $this->error_code = 'xml_parse_failed'; $this->sendHeader(false); return false; } $data = each($parsed_xml); $notificationType = $data['key']; $parsed_xml = $data['value']; $this->transaction_id = isset($parsed_xml['google-order-number']) ? $parsed_xml['google-order-number'] : false; $hash = isset($parsed_xml['order-summary']['shopping-cart']['items']['item']['merchant-item-id']) ? $parsed_xml['order-summary']['shopping-cart']['items']['item']['merchant-item-id'] : false; $order_state = isset($parsed_xml['order-summary']['financial-order-state']) ? $parsed_xml['order-summary']['financial-order-state'] : false; $totalcost = isset($parsed_xml['order-summary']['total-charge-amount']['value']) ? floatval($parsed_xml['order-summary']['total-charge-amount']['value']) : 0; $tax = isset($parsed_xml['order-summary']['order-adjustment']['total-tax']['value']) ? floatval($parsed_xml['order-summary']['order-adjustment']['total-tax']['value']) : 0; $currency = isset($parsed_xml['order-summary']['total-charge-amount']['currency']) ? strtolower($parsed_xml['order-summary']['total-charge-amount']['currency']) : 0; $cost = $totalcost - $tax; if ($this->transaction_id and $hash) { $this->paymentinfo = vB::getDbAssertor()->getRow('vBForum:getPaymentinfo', array('hash' => $hash)); if (!empty($this->paymentinfo)) { $sub = vB::getDbAssertor()->getRow('vBForum:subscription', array('subscriptionid' => $this->paymentinfo['subscriptionid'])); $subcost = unserialize($sub['cost']); if ($subcost) { $this->paymentinfo['currency'] = $currency; $this->paymentinfo['amount'] = $cost; switch ($notificationType) { case 'charge-amount-notification': if ($cost == floatval($subcost["{$this->paymentinfo[subscriptionsubid]}"]['cost'][$currency])) { $this->type = 1; } else { $this->error_code = 'invalid_payment_amount - XML: ' . $result . htmlspecialchars_uni(' SubmitURL: ' . $submitUrl . ' Headers: ' . implode(' ', $headers)); } break; case 'refund-amount-notification': case 'chargeback-amount-notification': $this->type = 2; break; case 'new-order-notification': case 'risk-information-notification': case 'authorization-amount-notification': $this->error_code = 'ignored_status_update'; $this->type = 3; break; default: } if ($this->type == 0 and $this->error_code == '') { switch ($order_state) { case 'CANCELLED': case 'CANCELLED_BY_GOOGLE': $this->type = 2; break; // Ignore these states // Ignore these states case 'PAYMENT_DECLINED': case 'REVIEWING': case 'CHARGEABLE': case 'CHARGING': case 'CHARGED': $this->type = 3; $this->error_code = 'ignored_status_update'; default: } } } else { $this->error_code = 'invalid_subscription - XML: ' . $result . htmlspecialchars_uni(' SubmitURL: ' . $submitUrl . ' Headers: ' . implode(' ', $headers)); } } else { $this->error_code = 'invalid_payment - XML: ' . $result . htmlspecialchars_uni(' SubmitURL: ' . $submitUrl . ' Headers: ' . implode(' ', $headers)); } $this->sendHeader(true); } else { $this->error_code = 'invalid_XML_response - XML: ' . $result . htmlspecialchars_uni(' SubmitURL: ' . $submitUrl . ' Headers: ' . implode(' ', $headers)); $this->sendHeader(false); return false; } $xml = new vB_XML_Builder(); $xml->add_group('notification-acknowledgment', array('xmlns' => 'http://checkout.google.com/schema/2', 'serial-number' => $this->registry->GPC['serial-number'])); $xml->close_group(); $xml->send_content_type_header(); $xml->send_content_length_header(); echo $xml->fetch_xml(); return $this->type > 0 and $this->type < 3; }