/**
	 * perform anti fraud checks on ipn values
	 *
	 * @param cbpaidPaymentNotification $ipn
	 * @param cbpaidPaymentBasket $paymentBasket
	 * @return bool|string
	 */
	private function _validateIPN( $ipn, $paymentBasket )
	{
		global $_CB_database;

		$matching						=	true;

		if ( in_array( $ipn->payment_status, array( 'Completed', 'Processed', 'Canceled_Reversal' ) ) ) {
			if ( $ipn->txn_type == 'subscr_payment' ) {
				$payments				=	$paymentBasket->getPaymentsTotals( $ipn->txn_id );

				if ( ( $paymentBasket->mc_amount1 != 0 ) && ( $payments->count == 0 ) ) {
					$amount				=	$paymentBasket->mc_amount1;
				} else {
					$amount				=	$paymentBasket->mc_amount3;
				}

				if ( sprintf( '%.2f', $ipn->mc_gross ) != sprintf( '%.2f', $amount ) ) {
					if ( ( sprintf( '%.2f', $ipn->mc_gross ) < sprintf( '%.2f', $amount ) ) || ( sprintf( '%.2f', ( $ipn->mc_gross - $ipn->tax ) ) != sprintf( '%.2f', $amount ) ) ) {
						if ( ( ! ( ( $paymentBasket->mc_amount1 != 0 ) && ( $payments->count == 0 ) ) ) && ( ( (float) sprintf( '%.2f', ( $ipn->mc_gross - abs( $ipn->tax ) ) ) ) < ( (float) sprintf( '%.2f', $amount ) ) ) ) {
							$matching	=	CBPTXT::P( 'amount mismatch on recurring_payment: $amount: [amount] != IPN mc_gross: [gross] or IPN mc_gross - IPN tax: [net] where IPN tax = [tax]', array( '[amount]' => $amount, '[net]' => ( $ipn->mc_gross - $ipn->tax ), '[gross]' => $ipn->mc_gross, '[tax]' => $ipn->tax ) );
						}
					}
				}
			} else {
				if ( sprintf( '%.2f', $ipn->mc_gross ) != sprintf( '%.2f', $paymentBasket->mc_gross ) ) {
					if ( ( sprintf( '%.2f', $ipn->mc_gross ) < sprintf( '%.2f', $paymentBasket->mc_gross ) ) || ( sprintf( '%.2f', $ipn->mc_gross - $ipn->tax ) != sprintf( '%.2f', $paymentBasket->mc_gross ) ) ) {
						$matching		=	CBPTXT::P( 'amount mismatch on webaccept: BASKET mc_gross: [basket_gross] != IPN mc_gross: [gross] or IPN mc_gross - IPN tax: [net] where IPN tax = [tax]', array( '[basket_gross]' => $paymentBasket->mc_gross, '[net]' => ( $ipn->mc_gross - $ipn->tax ), '[gross]' => $ipn->mc_gross, '[tax]' => $ipn->tax ) );
					}
				}
			}
		}

		if ( in_array( $ipn->txn_type, array( 'subscr_signup', 'subscr_modify', 'subscr_eot', 'subscr_cancel', 'subscr_failed', 'subscr_payment' ) ) ) {
			if ( ! $paymentBasket->isAnyAutoRecurring() ) {
				$matching				=	CBPTXT::P( 'paypal subscription IPN type [txn_type] for a basket without auto-recurring items', array( '[txn_type]' => $ipn->txn_type ) );
			}
		}

		if ( ! in_array( $ipn->txn_type, array( 'subscr_signup', 'subscr_modify', 'subscr_eot', 'subscr_cancel', 'subscr_failed' ) ) ) {
			if ( ( $ipn->txn_id === '' ) || ( $ipn->txn_id === 0 ) || ( $ipn->txn_id === null ) ) {
				$matching				=	CBPTXT::T( 'illegal transaction id' );
			} else {
				$countBaskets			=	$paymentBasket->countRows( "txn_id = '" . $_CB_database->getEscaped( $ipn->txn_id ) . "' AND payment_status = 'Completed'" );

				if ( ( $countBaskets == 1 ) && ( $paymentBasket->txn_id != $ipn->txn_id ) || ( $countBaskets > 1 ) ) {
					$matching			=	CBPTXT::P( 'transaction already used for [count] other already completed payment(s)', array( '[count]' => $countBaskets ) );
				}
			}
		}

		return $matching;
	}
	/**
	 * Checks against frauds for PDT and for IPN
	 *
  	 * In order to prevent fraud, PayPal recommends that your programs verify the following:
	 * When you receive a VERIFIED response, perform the following checks: 
	 *  1. Check that the payment_status is Completed. (NOT done here, but in UpdatePayment status)
	 *  2. If the payment_status is Completed, check the txn_id against the previous completed PayPal
	 *     transaction you have processed to ensure it is not a duplicate.
	 *  3. After you have checked the payment_status and txn_id, make sure the 
	 *     receiver_email is an email address registered in your PayPal account.
	 *  4. Check that the price, mc_gross, and currency, mc_currency, are correct for the item, 
	 *     item_name or item_number.
	 *  5. Check the the shared secret returned to you is correct.
	 * 
	 * @param  cbpaidPaymentNotification  $ipn            notification verified with paypal
	 * @param  cbpaidPaymentBasket        $paymentBasket  matched basket
	 * @param  string                     $cbpid          shared secret which should be returned by paypal
	 * @return boolean|string                             TRUE for no fraud detected, otherwise error TEXT
	 */
	private function _checkNotPayPalFraud( $ipn, $paymentBasket, $cbpid )
	{
		global $_CB_database;
		
		$matching = true;
		// 3) receiver_email is an email address registered in your PayPal account, to prevent the payment
		//    from being sent to a fraudulent account:
		$receiver_email			=	strtolower( trim( $this->getAccountParam( 'paypal_receiver_email' ) ) );
		$business				=	strtolower( trim( $this->getAccountParam( 'paypal_business' ) ) );
		if ( $receiver_email ) {
			if ( strtolower( $ipn->receiver_email ) != $receiver_email ) {
				// let's give a second, third and fourth chance to misconfigurations:
				if (   ( strtolower( $ipn->business ) != $business )
					&& ( strtolower( $ipn->receiver_email ) != $business )
					&& ( strtolower( $ipn->business ) != $receiver_email ) )
				{
					$matching	=	sprintf( "receiver_email mismatch: parametered business (%s) or receiver (%s) expected does not match IPN business (%s) or receiver (%s).", $business, $receiver_email, $ipn->business, $ipn->receiver_email );
				}
			}
		} else {
			if ( strtolower( $ipn->business ) != $business ) {
				// let's give a second chance to misconfigurations:
				if ( strtolower( $ipn->receiver_email ) != $business ) {
					$matching	=	sprintf( "business email mismatch: parametered business (%s) or receiver (%s) expected does not match IPN receiver (%s).", $business, $receiver_email, $ipn->receiver_email );
				}
			}
		}
		// 4) Check transaction details, such as the item number and price, to confirm that the price has not
		//    been changed: mc_gross, and currency, mc_currency, item_name or item_number:
		if ( in_array( $ipn->payment_status, array( 'Completed', 'Processed', 'Canceled_Reversal' ) ) ) {
			if ( $ipn->txn_type == 'subscr_payment' ) {
				$checkFields	=	array( 'mc_currency' => 100, 'item_name' => 127, 'item_number' => 127 );
				$txt_error		=	"currency, item name or number mismatch";
				// treat 'mc_gross':
				$payments		=	$paymentBasket->getPaymentsTotals( $ipn->txn_id );
				if ( ( $paymentBasket->mc_amount1 != 0 ) && ( $payments->count == 0 ) ) {
					$tobepaid	=	$paymentBasket->mc_amount1;
				} else {
					$tobepaid	=	$paymentBasket->mc_amount3;
				}
				if ( sprintf( '%.2f', $ipn->mc_gross ) != sprintf( '%.2f', $tobepaid ) ) {
					// try to check if it's a paypal tax added in paypal:
					if ( ( sprintf( '%.2f', $ipn->mc_gross ) < sprintf( '%.2f', $tobepaid ) )
					||   ( sprintf( '%.2f', $ipn->mc_gross - $ipn->tax ) != sprintf( '%.2f', $tobepaid ) ) )
					{
						// Final attempt to say "ok": if there is an increase in this recurring payment (not first one) done at paypal's side (20% per period max):
						if ( ( ! ( ( $paymentBasket->mc_amount1 != 0 ) && ( $payments->count == 0 ) ) ) && ( ( (float ) sprintf( '%.2f', $ipn->mc_gross - abs( $ipn->tax ) ) ) < (float) sprintf( '%.2f', $tobepaid ) ) ) {
							$matching	=	sprintf("amount mismatch on subscr_payment: tobepaid: %s != IPN mc_gross: %s or IPN mc_gross - IPN tax: %s where IPN tax = %s", $tobepaid, $ipn->mc_gross - $ipn->tax, $ipn->mc_gross, $ipn->tax );
						}
					}
				}
			} else {				// elseif ( $ipn->txn_type == 'web_accept' ) {
				if ( sprintf( '%.2f', $ipn->mc_gross ) != sprintf( '%.2f', $paymentBasket->mc_gross ) ) {
					// try to check if it's a paypal tax added in paypal:
					if ( ( sprintf( '%.2f', $ipn->mc_gross ) < sprintf( '%.2f', $paymentBasket->mc_gross ) )
					||   ( sprintf( '%.2f', $ipn->mc_gross - $ipn->tax ) != sprintf( '%.2f', $paymentBasket->mc_gross ) ) )
					{
						$matching	=	sprintf("amount mismatch on webaccept: BASKET mc_gross: %s != IPN mc_gross: %s or IPN mc_gross - IPN tax: %s where IPN tax = %s", $paymentBasket->mc_gross, $ipn->mc_gross - $ipn->tax, $ipn->mc_gross, $ipn->tax );
					}
				}
				$checkFields	=	array( 'mc_currency' => 100, 'item_name' => 127, 'item_number' => 127, 'quantity' => 127 );
				if ( $ipn->payment_status == 'Canceled_Reversal' ) {
					// for some reasons, Cancel_Reversal (we won!) don't provide quantity, so do not check: (bug #1099)
					unset( $checkFields['quantity'] );
				}
				$txt_error		=	"currency, item name or number or quantity mismatch";
			}
			foreach ( $checkFields as $cf => $csize ) {
				if ( !isset( $ipn->$cf) || !isset( $paymentBasket->$cf) || ( trim( substr( $ipn->$cf, 0, $csize ) ) != trim( substr( $paymentBasket->$cf, 0, $csize ) ) ) ) {
					// print_r($ipn); print_r($paymentBasket);
					$matching	=	$txt_error . ': ' . sprintf( "IPN %s (%s) does not match basket %s (%s) nor their trimmed sizes for IPN (%s) and basket (%s)", $cf, $ipn->$cf, $cf, $paymentBasket->$cf, trim( substr( $ipn->$cf, 0, $csize ) ), trim( substr( $paymentBasket->$cf, 0, $csize ) ) );
					break;
				}
			}
		} else {
			//TBD: see what to check for other events...
		}
		if ( in_array( $ipn->txn_type, array( 'subscr_signup', 'subscr_modify', 'subscr_eot', 'subscr_cancel', 'subscr_failed', 'subscr_payment' ) ) ) {
			if ( ! $paymentBasket->isAnyAutoRecurring() ) {
				$matching		=	sprintf( "paypal subscription IPN type %s for a basket without auto-recurring items", $ipn->txn_type );
			}
		}
	 	// 2) txn_id is not a duplicate to prevent someone from reusing an old, completed transaction:
		if ( ! in_array( $ipn->txn_type, array( 'subscr_signup', 'subscr_modify', 'subscr_eot', 'subscr_cancel', 'subscr_failed' ) ) ) {
			if ( ( $ipn->txn_id === '' ) || ( $ipn->txn_id === 0 ) || ( $ipn->txn_id === null ) ) {
				$matching		=	"illegal transaction id";
			} else {
				$countBaskets	=	$paymentBasket->countRows( "txn_id = '" . $_CB_database->getEscaped( $ipn->txn_id ) . "' AND payment_status = 'Completed'" );
				if ( ( $countBaskets == 1 ) && ( $paymentBasket->txn_id != $ipn->txn_id ) || ( $countBaskets > 1 ) ) {
					$matching	=	sprintf( "transaction already used for %d other already completed payment(s)", $countBaskets );
				}
			}
		}
		// 5) Check the the shared secret returned to you is correct.
		if ( $cbpid != $paymentBasket->shared_secret ) {
			$matching			=	sprintf( "shared secret '%s' returned by Paypal does not match the value we expected", htmlspecialchars( $cbpid ) );
		}
		return $matching;
	}