I am trying to create a method through which I can retrieve the data from the database of contacts saved by contact form 7 and pre-fill the fields after the users have previously selected, from a drop-down menu, the PayPal payment method and filled out the form , without then proceeding to the actual payment.
The flow to follow is this:
If the user has chosen to pay with PayPal and filled out the entire form, the "paid" item will be 0 and without making any redirects to the PayPal page
Then, when the user returns to the event card, we will show all the fields filled in previously and the "pay with paypal" button to complete the paypal payment. So the "paid" item will be 1.
So far I have used the following plugins to save the data of the completed forms Advanced CF7 DB and Contact Form CFDB7.
The use of one of these, or someone else, is indifferent to my goal
Here is the code with which so far I am able to populate the dynamic and hidden field [dynamichidden paid ""] when the user selects PayPal but without too much success:
add_action('wpcf7_posted_data', 'course_registration_actions_paypal', 10, 1);
function course_registration_actions_paypal($stato0){
$paypal["paymentmethod"] = '0';
$stato0[“paid”] = '0';
$stato1[“paid”] = '1';
if (isset($paypal[“paymentmethod]) && $stato0[“paid”] === '0') {
return $stato0;
} else {
return $stato1;
}
};
Eventually I managed to get it to work as I wanted, but there remains a redirect problem using the "redirection for contact form 7" plugin.
Always redirect on the first case (paypal) rather than differentiate.
I think the problem is 'wpcf7_posted_data' as without redirects they work fine.
This is my code:
add_action('wpcf7_posted_data','course_registration_actions_persist_payment_status', 10, 1);
function course_registration_actions_persist_payment_status($record){
$current_user = wp_get_current_user();
$userId = get_field('id__pro', 'user_' . $current_user->ID);
$eventId = get_field('id', false);
$records = WPCF7_ContactForm::find([
'ID-Course' => $eventId, // 2
'ID-User' => $userId, // 82994
]);
if ($record['paymentmethod'][0] == 0) {
// Paypal
$record['paymentmethod'] = 0;
// No Paid
$record['paid'] = 0;
} else {
// Bank
$record['paymentmethod'] = 1;
// Paid
$record['paid'] = 1;
}
return $record;
};
Related
For the Braintree_PaymentMethod::create() function, one of the options is:
'failOnDuplicatePaymentMethod', bool
If this option is passed and the payment method has already been added to the Vault, the request will fail. This option will not work with PayPal payment methods.
This appears to be a global compare. i.e. if the credit card information exists in the vault regardless of customer id this will fail.
Is there a way to check for duplicates on a particular customer?
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact support.
You and Evan are correct: this is the only pre-built way of failing on duplicate creates regardless of customer create. You could achieve what you are trying to do with your own automation, however.
To do this, simply collect the credit card unique ids that already exist from the customer object. Then when you create the new payment method, compare it with the existing cards:
function extractUniqueId($creditCard){
return $creditCard->uniqueNumberIdentifier;
}
$customer = Braintree_Customer::find('your_customer');
$unique_ids = array_map(extractUniqueId,$customer->creditCards);
$result = Braintree_PaymentMethod::create(array(
'customerId' => 'your_customer',
'paymentMethodNonce' => 'fake-valid-discover-nonce',
));
if ($result->success) {
if(in_array(extractUniqueId($result->paymentMethod), $unique_ids)) {
echo "Do your duplicate logic";
} else {
echo "Continue with your unique logic";
}
}
Depending on what you want to do, you could delete the new payment method or whatever else you need.
Checked with Braintree support--still not available out of the box:
If you use failOnDuplicatePaymentMethod any request to add duplicate payment method information into the Vault will fail.
We currently don’t have the functionality to prevent a customer from adding a duplicate card to their profile, while allowing duplicate cards to still be added under multiple profiles. If this is something you are interested in you will have to build out your own logic.
#Raymond Berg, I made soem changes in your code, Here is the updated code:
1. Used foreach instead of in_array
2. Also delete the added card If found duplicate
$customer = Braintree_Customer::find('your_customer');
$unique_ids = array_map(extractUniqueId,$customer->creditCards);
$result = Braintree_PaymentMethod::create(array(
'customerId' => 'your_customer',
'paymentMethodNonce' => 'fake-valid-discover-nonce',
));
if ($result->success) {
$cardAlreadyExist = false;
$currentPaymentMethod = $this->extractUniqueId($result->paymentMethod);
//The in_array function was not working so I used foreach to check if card identifier exist or not
foreach ($unique_ids as $key => $uid) {
if( $currentPaymentMethod == $uid->uniqueNumberIdentifier)
{
$cardAlreadyExist = true;
//Here you have to delete the currently added card
$payment_token = $result->paymentMethod->token;
Braintree_PaymentMethod::delete($payment_token);
}
}
if($cardAlreadyExist) {
echo "Do your duplicate logic";
} else {
echo "Continue with your unique logic";
}
}
Here is a .NET version. Not 100% complete, but a good starter for someone with the same situation. If you find any issues or suggestions please just edit this answer.
try
{
// final token value (unique across merchant account)
string token;
// PaymentCreate request
var request = new PaymentMethodRequest
{
CustomerId = braintreeID,
PaymentMethodNonce = nonce,
Options = new PaymentMethodOptionsRequest()
};
// try to create the payment without allowing duplicates
request.Options.FailOnDuplicatePaymentMethod = true;
var result = await gateway.PaymentMethod.CreateAsync(request);
// handle duplicate credit card (assume CC type in this block)
if (result.Errors.DeepAll().Any(x => x.Code == ValidationErrorCode.CREDIT_CARD_DUPLICATE_CARD_EXISTS))
{
// duplicate card - so try again (could be in another vault - ffs)
// get all customer's existing payment methods (BEFORE adding new one)
// don't waste time doing this unless we know we have a dupe
var vault = await gateway.Customer.FindAsync(braintreeID);
// fortunately we can use the same nonce if it fails
request.Options.FailOnDuplicatePaymentMethod = false;
result = await gateway.PaymentMethod.CreateAsync(request);
var newCard = (result.Target as CreditCard);
// consider a card a duplicate if the expiration date is the same + unique identifier is the same
// add on billing address fields here too if needed
var existing = vault.CreditCards.Where(x => x.UniqueNumberIdentifier == newCard.UniqueNumberIdentifier).ToArray();
var existingWithSameExpiration = existing.Where(x => x.ExpirationDate == newCard.ExpirationDate);
if (existingWithSameExpiration.Count() > 1)
{
throw new Exception("Something went wrong! Need to decide how to handle this!");
}
else
{
// delete the NEW card
await gateway.PaymentMethod.DeleteAsync(newCard.Token);
// use token from existing card
token = existingWithSameExpiration.Single().Token;
}
}
else
{
// use token (could be any payment method)
token = result.Target.Token;
}
// added successfully, and we know it's unique
return token;
}
catch (BraintreeException ex)
{
throw;
}
catch (Exception ex)
{
throw;
}
Available for cards now as stated here . Not applicable for Paypal , Gpay and other payment methods. But this requires us to send the braintree customerID along.
I have created a custom payment module and currently it calls validateOrder() after the redirection from the payment website, and this method creates the order, sends email etc. But the issue is if user closed the payment website before it can redirect back to the PrestaShop website the order won't be created in this case. So, I want to create an order(say with "pending" status) before I redirect to the payment website and after redirection from the payment website I can simply mark the same payment as done and send mails etc.
Currently for this I was trying to call validateOrder twice, once in hookdisplayPayment(here I set the status as "pending") and once after redirection. But now after redirection I am getting "The cart cannot be loaded, or an order has already been placed using this cart". I think that's because I can't update the same order twice using the same Card Id.
Note that I want to send the emails only once, once the payment is successful. Currently for this I am using a custom payment status with 'send_email' set to 0.
What's a good workaround for this?
I would like to support versions 1.5+ and 1.6+ if that matters.
A better way to do it than my first answer would be to create a override in your module of function validateOrder.
You will modify:
/** #var Order $order */
$order = new Order();
Into:
/** #var Order $order */
$order = new Order($this->currentOrder);
Then test if is loaded object, skip the part where it sets the order fields. If it's not loaded, set the order fields appropriately with the pending status.
Also test if $this->currentOrder is set where the email is sent, if it's not set skip the email part. If it's set it means the order is pending and you should change the status and send the email.
After you override the function, you can call validateOrder twice, before and after redirection.
You could try something like this:
Before making the redirection you can call once function validateOrder and set status as pending. This will set for your module the variable $this->currentOrder with the id of the pending order.
After redirection don't call again validateOrder, but create your own function to call, eg. validateOrderAfterRedirect in which you check that the payment was made and change the status of the current order. It will be something like this:
// your way of checking that te payment was made
$payment_completed = $this->paymentIsComplete();
if($payment_completed) {
$order = new Order($this->currentOrder);
if(Validate::isLoadedObject($order) && $order->getCurrentOrderState() == [id of pending status]) {
$order->setCurrentState([id of payment accepted status]);
}
}
Create an order with "pending payment" status before the website is redirected to payment system. Once the customer returns the system should just change the payment status to "completed". If the customer closes the payment site, the status will remain "pending" and should be manually updated after checking the payment system.
Many payment gateways provide a mechanism where on completed or failed payment they post data including amount paid and cart ID to a URL you supply to them.
When you process this information using a server-side script at that stage you can validate the order. This should happen before the user is redirected back to your website. Once they are redirected to your site it will already have acknowledged payment in the background.
The reason this method is preferred is that it is the only way to ensure the customer cannot manipulate a URL to make your store think they have paid for an order when in fact no money has changed hands, after which you could end up shipping products for free.
You can do it by adding some like this
$result = $this->validateOrder((int) $cart->id, Configuration::get('PPQ_CREATED_STATUS'), $total, $this->displayName, NULL, array(), (int) $currency->id, false, $customer->secure_key);
in any plays where you need before redirection(where $this is payment module instance)
And after redirection to confirm page I have use wrote this
public function hookPaymentReturn($params)
{
$id_module = (int) Tools::getValue('id_module');
if ($id_module === (int) $this->id) {
$orderHistory = new OrderHistory();
$orderHistory->changeIdOrderState(Configuration::get('PPQ_SUCCESS_STATUS'), $params['objOrder']);
}
}
For mail sending you can configure needed order status
For my case (need work only with paypal, I have change write my own one page checkout module and write my own payment module and befour redirect to paypal I have wrote this
public function hookPayment($params)
{
$customer = &$this->context->customer;
$cart = &$this->context->cart;
$currency = &$this->context->currency;
if (
$customer->isLogged(true) &&
$cart->nbProducts()
) {
$total = (float) $cart->getOrderTotal(true, Cart::BOTH);
$result = $this->validateOrder((int) $cart->id, Configuration::get('PPQ_CREATED_STATUS'), $total, $this->displayName, NULL, array(), (int) $currency->id, false, $customer->secure_key);
if ($result) {
if (!Configuration::get('PPQ_TEST_MODE')) {
$paypal_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=' . Configuration::get('PPQ_PROFILE');
} else {
$paypal_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_xclick&business=' . Configuration::get('PPQ_PROFILE');
}
$order_confirmation_url = $this->context->link->getPageLink('order-confirmation', null, null, array(
'id_cart' => (int) $cart->id,
'id_module' => (int) $this->id,
'id_order' => (int) $this->currentOrder,
'key' => $customer->secure_key,
));
$this->context->smarty->assign(array(
'paypal_url' => $paypal_url,
'order_confirmation_url' => $order_confirmation_url,
'order_id' => (int) $this->currentOrder,
'shop_name' => $this->context->shop->name,
'total_without_shipping' => Tools::convertPriceFull((float) $cart->getOrderTotal(true, Cart::BOTH_WITHOUT_SHIPPING)),
'total_shipping' => Tools::convertPriceFull((float) $cart->getOrderTotal(true, Cart::ONLY_SHIPPING)),
'currency_iso' => Tools::strtoupper($currency->iso_code)
));
return $this->display(__FILE__, 'paypalquick.tpl');
} else {
$this->context->controller->errors[] = $this->l('Can\'t create order. Pleas contact with us');
}
} else {
$this->context->controller->errors[] = $this->l('Problem with loginin or cart empty');
}
}
and tpl
<form id="paypalquick" action="{$paypal_url}" method="post" enctype="multipart/form-data">
<input type="hidden" value="{l s='%s order #%s' sprintf=[$shop_name|escape:'html':'UTF-8', $order_id|intval] mod='paypalquick'}" name="item_name"/>
<input type="hidden" value="{$total_without_shipping}" name="amount"/>
<input type="hidden" value="{$total_shipping}" name="shipping"/>
<input type="hidden" value="{$currency_iso}" name="currency_code"/>
<input type="hidden" value="{$order_confirmation_url}" name="return"/>
<div class="text-center">
<button class="submit">{l s='Go to PayPal for payment' mod='paypalquick'}</button>
</div>
But it was my private cas you can't use it on default but you can see how to make it.
I think we need you to call another hook (that you create) at the time of validation on the site (before leaving that matter) who put a pending status, and keep ValidateOrder hook () to to payment confirmed
Regards,
Arthur
I am using paypal adaptive payments for my website. I have many sellers and different products. when I am as a user try to buy any product from my website then I can't see product name in Paypal form summary instead there is the name and surname of the seller.
Let me know please which parameter is being used to Pass product name ..
Here is the screenshot
With Adaptive Payments you can't send itemized details in the Pay request itself. Instead, you have to call Pay like usual, but then follow that up with a call to SetPaymentOptions. With that you'll pass in the PayKey you get back from the Pay request, and then you can setup all the additional details like itemized info that SetPaymentsOptions provides.
Then you would redirect to PayPal after that, and it should show you what you're after.
With Adaptive Payments, the item details you set with SetPaymentOptions are only displayed to the customer via Embedded flow.
The Embedded flow uses either a lightbox or a minibrowser for the checkout pages.
Here’s a technical instruction on how to implement the embedded flow in your front-end page, https://developer.paypal.com/docs/classic/adaptive-payments/ht_ap-embeddedPayment-curl-etc/
I am having the same issue. It looks like it works only in an Embedded Payment Flow.
Embedded Payment Flow Using Adaptive Payments
$receiverOptions = new PayPal\Types\AP\ReceiverOptions();
$setPaymentOptionsRequest->receiverOptions[] = $receiverOptions;
$receiverOptions->description = 'Description';
$invoiceItems = array();
$item = new PayPal\Types\AP\InvoiceItem();
$item->name = 'Item Name';
$item->price = 10;
$item->itemPrice = 10;
$item->itemCount = 1;
$invoiceItems[] = $item;
$receiverOptions->invoiceData = new PayPal\Types\AP\InvoiceData();
$receiverOptions->invoiceData->item = $invoiceItems;
$receiverId = new PayPal\Types\AP\ReceiverIdentifier();
$receiverId->email = 'email#domain.com';//Change it
$receiverOptions->receiver = $receiverId;
$setPaymentOptionsRequest->payKey = $_POST['payKey'];
$servicePaymentOptions = new PayPal\Service\AdaptivePaymentsService($config);
try {
/* wrap API method calls on the service object with a try catch */
$responsePaymentOptions = $servicePaymentOptions->SetPaymentOptions($setPaymentOptionsRequest);
print_r($responsePaymentOptions); die;
} catch(Exception $ex) {
//error
}
if (isset($responsePaymentOptions) && $responsePaymentOptions->responseEnvelope->ack == "Success")
{
//Success
}
I have a form at my website where the user can request for information, but there's a catch. Inside the form I have a checkbox and when the user selects it, the email must to be sent to another place. (See the screenshots)
What's happening is: Gravity form sends both emails when both rules match, but I only wanna send one email based on the priority.
How can I do that?
I don't believe the routing will take into account AND conditions for the individual rules.
You can however, use the following filter to do further modifications to the e-mail before it is being sent:
add_filter("gform_notification", "my_custom_function", 10, 3);
Or, for a specific form (i.e. id = 42):
add_filter("gform_notification_42", "my_custom_function", 10, 3);
Source: http://www.gravityhelp.com/documentation/page/Gform_notification
Update
An example of accessing the fields would look like this:
add_filter('gform_notification_42', 'updateNotificationForForm42', 10, 3);
function updateNotificationForForm42( $notification, $form, $entry ) {
$fields = $form['fields'];
foreach( $fields as $field ) {
// Here you need to provide the field with some kind of identifying mark (e.g., Admin Label).
// Below assumes the field you're interested in has an admin label of 'Test Me'
if( $field['adminLabel'] == 'Test Me' ) {
$fieldValue = rgpost("input_{$field['id']}");
}
}
}
The Gravity Forms developer docs have a lot of examples of how to customize via actions/filters.
See Also: http://www.gravityhelp.com/documentation/page/Fields
How to implement two type of registration like student and teacher?
I need two type of registration one for Teacher and one for Student. Both are different registration and both have different roles. Is it possible in Drupal? And also I need registering Student there is no admin approval but for Teacher registration, admin approval is required. How can I achieve this in Drupal 6?
In the custom user registration form add one more select box field with ROLE TYPE (student, teacher).Then on the submit hook check like shown below.
function add_student_form_submit($form, &$form_state) {
$fields = array();
$fields['is_new'] = true;
$fields['name'] = $form_state['values']['user_name'];
$fields['pass'] = $form_state['values']['pass'];
$role_type = $form_state['values']['role_type'];
//Add the user to the corresponding role
$fields['roles'] = array($role_type)
//here you can achieve the thing which you want.If the role is a teacher then set
//status = 0, else status = 1
if($role_type == 'student')
$fields['status'] = 1;
else
$fields['status'] = 0; // $user = user_save(drupal_anonymous_user(), $fields); //This works in D7
$user = user_save('', $fields); //pretty sure this is what works in D6 }
If the user is the teacher you should go to http://localhost/domain_name/admin/user/user. Here you can filter the Inactive users and activate them.
hey you can use below modules for creating multilevel registration.
http://drupal.org/project/content_profile
http://drupal.org/project/autoassignrole
above modules help you to create multilevel registration form in site. from content profile you can create form and also give auto assign role to student.
As far as i'm aware, drupal doesn't provide any mechanism for having multiple types of registration forms. However you can fairly easily create your own registration form from scratch. All you really need is the user_save function to create a new user. See the sample code below as part of a form_submit hook
function add_student_form_submit($form, &$form_state) {
$fields = array();
$fields['is_new'] = true;
$fields['name'] = $form_state['values']['user_name'];
$fields['pass'] = $form_state['values']['pass'];
$fields['status'] = 1;
// $user = user_save(drupal_anonymous_user(), $fields); //This works in D7
$user = user_save('', $fields); //pretty sure this is what works in D6
}
Using this you can create whatever custom logic you want for each form