I want to integrate paypal advanced checkout to my website,
but I have problems with loading the custom form elements
this is my form page:
<script src="https://www.paypal.com/sdk/js?components=buttons,hosted-fields&client-id={{env('PAYPAL_CLIENT_ID')}}" data-client-token="{{$client_token}}" ></script>
let orderId;
// Displays PayPal buttons
paypal.Buttons({
style: {
layout: 'horizontal'
},
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: "1.00"
}
}]
});
},
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
window.location.href = '/success.html';
});
}
}).render("#paypal-button-container");
// If this returns false or the card fields aren't visible, see Step #1.
if (paypal.HostedFields.isEligible()) {
// Renders card fields
paypal.HostedFields.render({
// Call your server to set up the transaction
createOrder: function () {
return fetch('/paypal/process/', {
method: 'post'
}).then(function(res) {
return res.json();
}).then(function(orderData) {
orderId = orderData.id;
return orderId;
});
},
..........
} else {
// Hides card fields if the merchant isn't eligible
document.querySelector("#card-form").style = 'display: none';
}
enter where it says (// Hides card fields if the merchant isn't eligible)
I have the business account enabled and I previously generated the client token correctly
Before you can accept card payments with Advanced, you must request advanced debit and credit card processing. This is documented in the first section of the integration guide, with a link to do it for sandbox mode (you'll need to do it separately for live)
Related
Hi Stackoverflow community,
I need help in loading Paypal messages in web components.
After loading the paypal SDK, I am trying to include the Paypal pay later messages with the code below.
window.paypal.Messages({
amount: this.amount,
placement: "product",
style: {
layout: "text",
logo: {
type: "inline",
},
},
}).render(this.shadowRoot!.querySelector("#paypal-message") as HTMLElement);
I am getting the following error in the browser console.
paypal_messages_not_in_document
description: "Container must be in the document."
timestamp: "1651659388515"
I am able to load the paypal buttons with the same logic.
window.paypal.Buttons({ ....... }).render(this.shadowRoot!.querySelector("#paypal-button") as HTMLElement);
Below is the web component in lit element framework.
import { customElement, html, internalProperty, property } from "lit-element";
import { RmsBookstoreAddress, PaymentDetails, PaymentType } from "../../../../features/shop-checkout";
import { BaseComponent } from "../../../../services/base-component";
import { booleanConverter } from "../../../../services/converters";
import { waitForElementsToLoad } from "../../../../services/delayUtil";
import emit from "../../../../services/events";
/* WARNING: Do NOT import directly from "braintree-web", it causes the bundle size to increase dramatically. */
import client from "braintree-web/client";
import dataCollector from "braintree-web/data-collector";
import paypalCheckout, { PayPalCheckoutTokenizationOptions } from "braintree-web/paypal-checkout";
/**
* Configure and load Paypal button.
* Saves paypal payment details in redux.
*/
#customElement("paypal-button")
export default class PaypalButton extends BaseComponent {
/**
* Flag to indicate the checkout contains shippable items.
*/
#property({ converter: booleanConverter })
shippable = false;
/**
* Braintree Client token to initialize Paypal button.
*/
#property()
clientToken = "";
/**
* Currency for Paypal transaction.
*/
#property()
currency = "USD";
/**
* Option to set the description of the preapproved payment agreement visible to customers in their PayPal profile during Vault flows. Max 255 characters.
*/
#property()
billingAgreementDescription = "";
/**
* Transaction amount to be displayed in Paypal.
*/
#property({type: Number })
amount = 0;
/**
* Value to override paypal shipping address.
*/
#property({attribute: false, type: Object})
shippingAddress;
/**
* Allow PayPal to Capture the shipping address.
*/
#property({ converter: booleanConverter })
usePayPalShippingAddress = false;
#property({ converter: booleanConverter})
userAutoLogin = false;
/**
* Billing address returned by Paypal
*/
#internalProperty()
private internalBillingAddress: RmsBookstoreAddress | undefined;
/**
* Shipping address returned by Paypal
*/
#internalProperty()
private internalShippingAddress: RmsBookstoreAddress | undefined;
/**
* Paypal payment details
*/
#internalProperty()
private paymentDetails: PaymentDetails | undefined;
renderComp() {
return html`
<div id="paypal-button"></div>
<div id="paypal-message"></div>
`;
}
/**
* Wait for the paypal button place order to render before adding the paypal button to it.
* #param _changedProperties
*/
async firstUpdated(_changedProperties: Map<string | number | symbol, unknown>) {
super.firstUpdated(_changedProperties);
await waitForElementsToLoad(this.shadowRoot!, [
"#paypal-button",
]);
this.setupPaypalButton();
}
setupPaypalButton(){
//create braintree client instance
client.create({
authorization: this.clientToken
}).then( clientInstance =>{
//collect device data
dataCollector.create({
client: clientInstance,
}).then((dataCollectorInstance)=>{
const paypalPaymentDeviceData = dataCollectorInstance.deviceData;
//paypal button shipping config
let shippingConfig = {};
let intent: "capture" | "authorize" = "capture" // for digital products intent is capture
if(this.shippable){
intent = 'authorize'; // for physical or mixed cart products intent is authorize
if(!this.usePayPalShippingAddress && this.shippingAddress){
shippingConfig = {
enableShippingAddress: true,
shippingAddressEditable: false,
shippingAddressOverride: {
recipientName: `${this.shippingAddress.firstName} ${this.shippingAddress.lastName}`,
line1: `${this.shippingAddress.address1}`,
line2: `${this.shippingAddress.address2 ? this.shippingAddress.address2 : ''}`,
city: `${this.shippingAddress.city}`,
countryCode: `${this.shippingAddress.country}`,
postalCode: `${this.shippingAddress.zipCode}`,
state: `${this.shippingAddress.state}`,
phone: `${this.shippingAddress.phoneNumber}`
}
}
} else if (this.usePayPalShippingAddress) {
shippingConfig = {
enableShippingAddress: true,
shippingAddressEditable: true
}
}
}
//create paypal button
paypalCheckout.create({
client: clientInstance,
autoSetDataUserIdToken: this.userAutoLogin
}).then( paypalCheckoutInstance => {
paypalCheckoutInstance.loadPayPalSDK({
components: 'buttons,messages',
currency: this.currency,
intent: intent,
}).then( () => {
window.paypal.Messages({
amount: this.amount,
placement: "product",
style: {
layout: "text",
logo: {
type: "inline",
},
},
}).render(this.shadowRoot!.querySelector("#paypal-message") as HTMLElement);
window.paypal.Buttons({
fundingSource: window.paypal.FUNDING.PAYPAL,
createOrder: () => {
return paypalCheckoutInstance.createPayment({
flow: 'checkout',
amount: this.amount,
currency: this.currency,
requestBillingAgreement: true,
billingAgreementDescription: this.billingAgreementDescription,
intent: intent,
...shippingConfig,
});
},
onApprove: (_data: PayPalCheckoutTokenizationOptions, _actions: any) => {
return paypalCheckoutInstance.tokenizePayment(_data).then( payload => {
const paypalBillingAddress: any = payload.details.billingAddress;
this.internalBillingAddress = {
firstName: payload.details.firstName,
lastName: payload.details.lastName,
phoneNumber: payload.details.phone ? payload.details.phone : '',
country: paypalBillingAddress.countryCode,
address1: paypalBillingAddress.line1,
address2: paypalBillingAddress.line2,
city: paypalBillingAddress.city,
state: paypalBillingAddress.state,
zipCode: paypalBillingAddress.postalCode,
};
this.paymentDetails = {
paymentType: PaymentType.BRAINTREE_PAYPAL,
paymentNonce: payload.nonce,
deviceData: paypalPaymentDeviceData,
paypalEmail: payload.details.email
}
if (this.usePayPalShippingAddress) {
const paypalShippingAddress: any = payload.details.shippingAddress;
this.internalShippingAddress = {
firstName: payload.details.firstName,
lastName: payload.details.lastName,
phoneNumber: payload.details.phone ? payload.details.phone : '',
country: paypalShippingAddress.countryCode,
address1: paypalShippingAddress.line1,
address2: paypalShippingAddress.line2,
city: paypalShippingAddress.city,
state: paypalShippingAddress.state,
zipCode: paypalShippingAddress.postalCode,
};
}
emit({
element: this,
name: `mhecomm-paypal-checkout-info-collected`,
detail: {
billingAddress: this.internalBillingAddress,
paymentDetails: this.paymentDetails,
shippingAddress: this.internalShippingAddress,
},
});
});
},
onCancel: (data: any) => {
console.log('PayPal payment cancelled', JSON.stringify(data));
},
onError: (err: any) => {
console.error('PayPal error', err);
}
}).render(this.shadowRoot!.querySelector("#paypal-button") as HTMLElement);
//console.log(paypalObj)
//console.log(this.shadowRoot!.querySelector("#paypal-message") as HTMLElement)
});
});
});
});
}
}
declare global {
interface HTMLElementTagNameMap {
'paypal-button': PaypalButton;
}
}
How is 'renderComp' called? Not sure if that's a standard callback for your framework, but based on the error it would appear the that the <div> for displaying the messages does not exist in the DOM at the time the messages are being rendered:
window.paypal.Messages({...
}).render(this.shadowRoot!.querySelector("#paypal-message") as HTMLElement);
You should work to ensure the <div> with that id exists. If necessary, you can add some logging above the window.paypal.Messages invocation to verify the container with that id exists at the moment that JS is being run.
Also the problem could be related to the use of a Shadow DOM; ensure that the #paypal-message container can be found in the main page's DOM which is where window.paypal.Messages will look for it.
I want to add some additional values(price, brand) to the existing datalayer event(productCLick event)in my magento 2.3.6.
function myFunction(id,price,brand,productUrl) {
dataLayer.push({ ecommerce: null }); // Clear the previous ecommerce object.
dataLayer.push({
'event': 'productClick',
'ecommerce': {
'click': {
'actionField': {'list': 'Catalog Page'},
'products': [{
'id':id,
'price': price, // Name or ID is required.
'brand': brand
}]
}
},
'eventCallback': function() {
document.location = productUrl
}
});
}
I implemented using onclick and am sending the custom data via parameters
I am currently checking integration process of v2 express checkout and lot of documentation making confusion.
So far i am using checkout.js from paypal domain and using below code for order creation in client side as below
paypal.Button.render({
env: 'sandbox', // Or 'sandbox',
commit: true, // Show a 'Pay Now' button
style: {
color: 'gold',
shape: 'rect',
label: 'paypal',
size: 'medium',
tagline: false,
width: 150
},
payment: function (data, actions) {
/* Set up a url on your server to create the payment */
var CREATE_URL = '/paypal/createpaypalPayment';
/* Make a call to your server to set up the payment */
return paypal.request.post(CREATE_URL)
.then(function ({ result }) {
var test = JSON.parse(result);
return test.id;
});
},
onAuthorize: function (data, actions) {
/* Set up a url on your server to execute the payment */
var EXECUTE_URL = '/paypal/executepaypalPayment';
/* Set up the data you need to pass to your server */
var data = {
paymentID: data.paymentID,
payerID: data.payerID
};
return paypal.request.post(EXECUTE_URL, data)
.then(function (res) {
return null;
});
}
}, '#paypalcheckout');
});
Please find the server side code for create order.
[HttpPost]
public JsonResult createpaypalpayment()
{
var client = new WebClient();
string credentials = clientid + secretid;
client.Headers.Add("authorization", "Basic " + credentials);
client.Headers.Add("content-type", "application/json");
client.Headers.Add("accept-language", "en_US");
client.Headers.Add("accept", "application/json");
var body = #"{
""intent"": ""AUTHORIZE"",
""purchase_units"": [{
""amount"": {
""currency_code"": ""USD"",
""value"": ""100.00""
}
}]
}";
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var response = client.UploadString("https://api.sandbox.paypal.com/v2/checkout/orders/", "POST", body);
return Json(new { result = response }, JsonRequestBehavior.AllowGet);
}
}
}
I referred below documentation
https://developer.paypal.com/docs/archive/orders-integration-guide/#integration-steps
I am struct with how to show order details after order creation and get approval from customer end ?
Can anyone provide suggestion for this.
Thanks in advance
checkout.js is deprecated and that documentation is archived, so my suggestion is that you follow the current documentation on developer.paypal.com
In your other question, links were provided to that documentation.
I have been continuously getting an email by brain tree on PCI Compliance regards and need confirmation on following two things which have been asked.
What is the Braintree payment integration method on our website? (Hint: It’s one of these)
Drop in UI or hosted field
Braintree SDK Custom integration
Following is the javascript code we've used . I went through the Braintree site on this regards but couldn't conclude upon this.
Additional Notes : We've made some changes on braintree vendor file.
var subscribed_user = "1";
$('#cc').on('click', function (e) {
$('#cc-info').show().attr('aria-hidden', true).css('visibility', 'visible');
});
var button = document.querySelector('#paypal-button');
var button1 = document.querySelector('#card-button');
var form = document.querySelector('#checkout-form');
var authorization = 'AuthHeaderxxxxxxxx=';
// Create a client.
braintree.client.create({
authorization: authorization
}, function (clientErr, clientInstance) {
// Stop if there was a problem creating the client.
// This could happen if there is a network error or if the authorization
// is invalid.
if (clientErr) {
console.error('Error creating client:', clientErr);
return;
}
/* Braintree - Hosted Fields component */
braintree.hostedFields.create({
client: clientInstance,
styles: {
'input': {
'font-size': '10pt',
'color': '#e3e3e3 !important; ',
'border-radius': '0px'
},
'input.invalid': {
'color': 'red'
},
'input.valid': {
'color': 'green'
}
},
fields: {
number: {
selector: '#card-number',
placeholder: '4111 1111 1111 1111',
},
cvv: {
selector: '#cvv',
placeholder: '123'
},
expirationDate: {
selector: '#expiration-date',
placeholder: '10/2019'
}
}
}, function (hostedFieldsErr, hostedFieldsInstance) {
if (hostedFieldsErr) { /*Handle error in Hosted Fields creation*/
return;
}
button1.addEventListener('click', function (event) {
event.preventDefault();
hostedFieldsInstance.tokenize(function (tokenizeErr, payload) {
if (tokenizeErr) { /* Handle error in Hosted Fields tokenization*/
document.getElementById('invalid-field-error').style.display = 'inline';
return;
}
/* Put `payload.nonce` into the `payment-method-nonce` input, and thensubmit the form. Alternatively, you could send the nonce to your serverwith AJAX.*/
/* document.querySelector('form#bt-hsf-checkout-form input[name="payment_method_nonce"]').value = payload.nonce;*/
document.querySelector('input[name="payment-method-nonce"]').value = payload.nonce;
form.submit();
button1.setAttribute('disabled', 'disabled');
});
}, false);
});
// Create a PayPal component.
braintree.paypal.create({
client: clientInstance,
paypal: true
}, function (paypalErr, paypalInstance) {
// Stop if there was a problem creating PayPal.
// This could happen if there was a network error or if it's incorrectly
// configured.
if (paypalErr) {
console.error('Error creating PayPal:', paypalErr);
return;
}
if ($('select#paypal-subs-selector option:selected').val() == '') {
button.setAttribute('disabled', 'disabled');
}
$('select#paypal-subs-selector').change(function () {
if ($('select#paypal-subs-selector option:selected').val() == '') {
button.setAttribute('disabled', 'disabled');
} else {
// Enable the button.
button.removeAttribute('disabled');
}
});
button.addEventListener('click', function () {
if(subscribed_user) {
// Popup Error for changing subscription.
swal({
html: true,
title: "",
text: "You are cancelling in the middle of subscription.<br/>If you do so you will not be refunded remaining days of your subscription.",
confirmButtonColor: '#605ca8',
confirmButtonText: 'Yes',
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "Proceed !",
closeOnConfirm: true
}, function (isConfirm) {
if (isConfirm) {
show_payment_methods(paypalInstance);
}
});
} else{
show_payment_methods(paypalInstance);
}
}, false);
});
});
Any help would be highly appreciated.
Your code says Braintree - Hosted Field component And you don’t use anything like this which I found by searching “Braintree api”. I think you’re safe to say you use hosted fields.
I am using braintree paypal checkout for the payment, payment is working fine, but not able to get response of that, here is my code for that
<script type="text/javascript">
var form = document.querySelector('#payment-form');
var client_token = "<?php echo \Braintree\ClientToken::generate(); ?>";
braintree.dropin.create({
authorization: client_token,
selector: '#bt-dropin',
paypal: {
flow: 'vault',
onSuccess: function (nonce, email) {
alert('sdsdsd123');
console.log(JSON.stringify(nonce));
},
},
}, function (createErr, instance) {
if (createErr) {
console.log('Error', createErr);
return;
}
form.addEventListener('submit', function (event) {
event.preventDefault();
instance.requestPaymentMethod(function (err, payload) {
if (err) {
console.log('Error', err);
return;
} else {
console.log("Payment confirmation");
console.log(payload);
}
// Add the nonce to the form and submit
document.querySelector('#nonce').value = payload.nonce;
form.submit();
});
});
},
);
var checkout = new Demo({
formID: 'payment-form'
});
But not able to get response in onsuccess function, can anyone please tell me how cani get this success response,
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact support.
It looks like you may be confusing the implementation of PayPal within the Braintree JSv2 Drop-In UI with the Braintree JSv3 Drop-In UI. The onSuccess option is not supported in JSv3. The full list of configuration options of the PayPal object in JSv3 is available here.
Based on the code you provided, I would suggest removing your onSuccess callback function. You should still be able to achieve your desired result by placing that code in your instance.requestPaymentMethod callback function like so:
<script type="text/javascript">
var form = document.querySelector('#payment-form');
var client_token = "<?php echo \Braintree\ClientToken::generate(); ?>";
braintree.dropin.create({
authorization: client_token,
selector: '#bt-dropin',
paypal: {
flow: 'vault'
}
}, function (createErr, instance) {
if (createErr) {
console.log('Error', createErr);
return;
}
form.addEventListener('submit', function (event) {
event.preventDefault();
instance.requestPaymentMethod(function (err, payload) {
if (err) {
console.log('Error', err);
return;
}
console.log("Payment confirmation");
console.log(payload);
alert('sdsdsd123');
console.log(payload.nonce);
// Add the nonce to the form and submit
document.querySelector('#nonce').value = payload.nonce;
form.submit();
});
});
});
</script>