我试图将Stripe集成到我的JSF应用程序中,并且很难导航离开"添加信用卡"页。一切正常,除非用户点击提交后,页面不会离开。
以下是addCreditCard.xhtml facelet。添加javascript逻辑作为提交eventListener并使用onclick="#{stripeCCBean.update()}"
触发bean更新方法()是我获得javascript成功创建令牌的唯一方法(如果jc由onclick触发,则createToken方法将失败一些未知的原因)并让bean识别隐藏的字段。
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
template="/WEB-INF/template.xhtml"
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<head>
<title>Facelet Title</title>
<link rel="stylesheet" type="text/css" href="/css/StripeCCTokenize.css"/>
<script src="https://js.stripe.com/v3/" type="text/javascript"></script>
<script src="https://code.jquery.com/jquery-3.2.0.js" type="text/javascript"></script>
<script src="/js/StripeCCTokenize.js" type="text/javascript"></script>
</head>
<h:body>
<h:form id="addCC" pt:action="/secure/addCreditCard.xhtml" pt:method="POST">
<h:inputHidden id="cardholder-name" value="#{userManagerBean.user.fullName}"/>
We loaded your customer details (name, email and customer ID) from the backend database:
<label>
Hello #{userManagerBean.user.firstName} #{userManagerBean.user.lastName}
</label>
<label>
E-Mail - #{userManagerBean.user.email}
</label>
<label>
Stripe Customer ID - #{userManagerBean.stripeUser.id}
</label>
<h:outputText value="Please enter the requested credit card and billing information below"/>
<span>Address</span>
<h:panelGrid columns="2">
<h:outputText value="Address" />
<h:inputText class="field" id="address1" value="#{stripeCCBean.card.address1}" pt:placeholder="Street address"/>
<h:outputText value="Address"/>
<h:inputText class="field" id="address2" value="#{stripeCCBean.card.address2}" pt:placeholder="Street address"/>
<h:outputText value="City" />
<h:inputText class="field" id="city" value="#{stripeCCBean.card.city}" pt:placeholder="city"/>
<h:outputText value="State" />
<h:inputText class="field" id="state" value="#{stripeCCBean.card.state}" pt:placeholder="state"/>
<h:outputText value="zip" />
<h:inputText class="field" id="address-zip" value="#{stripeCCBean.card.zipcode}" pt:placeholder="zipcode"/>
<h:outputText value="cc"/>
</h:panelGrid>
<div id="card-element" class="field"></div>
<h:commandButton value="Add Credit Card" onclick="#{stripeCCBean.update()}" type="submit" id="addButton"/>
</h:form>
</h:body>
这是StripeCCTokenize.js:
var stripe; var card;
$(document).ready(function () {
stripe = Stripe('pk_test_key');
var elements = stripe.elements();
card = elements.create('card', {
hidePostalCode: true,
style: {
base: {
iconColor: '#F99A52',
color: '#32315E',
lineHeight: '48px',
fontWeight: 400,
fontFamily: '"Helvetica Neue", "Helvetica", sans-serif',
fontSize: '15px',
'::placeholder': {
color: '#CFD7DF'
}
}
}
});
card.mount('#card-element');
function stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
var form = document.getElementById('addCC');
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
}
function setOutcome(result) {
if (result.token) {
// Use the token to create a charge or a customer
// https://stripe.com/docs/charges
console.log("Token: " + result.token.id);
stripeTokenHandler(result.token);
}
}
card.on('change', function (event) {
setOutcome(event);
});
document.querySelector('form').addEventListener('submit', function (e) {
e.preventDefault();
var extraDetails = {
address_line1: document.getElementById('addCC:address1').value,
address_line2: document.getElementById('addCC:address2').value,
address_city: document.getElementById('addCC:city').value,
address_state: document.getElementById('addCC:state').value,
address_zip: document.getElementById('addCC:address-zip').value,
name: document.getElementById('addCC:cardholder-name').value
};
console.log(extraDetails);
stripe.createToken(card, extraDetails).then(setOutcome);
});
});
这是stripeCCBean类:
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
@RequestScoped
@ManagedBean(name = "stripeCCBean")
public class StripeCCBean implements Serializable {
StripeCard card;
@ManagedProperty(value = "#{stripeServiceBean}")
private StripeServiceBean stripeServiceBean;
@ManagedProperty(value = "#{userManagerBean}")
private UserManagerBean userManagerBean;
@PostConstruct
public void init() {
System.out.println("StripeCCBean.init()");
card = new StripeCard();
card.setName(userManagerBean.getUser().getFullName());
}
public void update() throws IOException {
String token = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("stripeToken");
if (token == null) {
return;
}
System.out.println("StripeCCBean.update()");
System.out.println("Token: " + token);
System.out.println("Card: " + card);
try {
StripeService.addCard(userManagerBean.getStripeUser().getId(), token);
} catch (AuthenticationException | APIConnectionException | CardException | APIException | InvalidRequestException ex) {
ex.printStackTrace();
}
}
}
我尝试将action="#{stripeCCBean.viewAccount()}"
添加到<h:commandButton .../>
以及StripeCCBean的相应方法:
public String viewAccount() {
return "AccountView";
}
然而,表单只是运行Javascript,调用stripeCCBean.update()(一切正常),然后停留在该页面上。客户信息字段不会被清除,但信用卡元素会清除。
我尝试添加FacesContext.getCurrentInstance().getExternalContext().redirect("/secure/AccountView.xhtml");
以及
对于stripeCCBean.update()方法FacesContext.getCurrentInstance().getExternalContext().dispatch("/secure/AccountView.xhtml");
并且都不起作用。事实上,他们抛出异常。
谁能看到我做错了什么?如果我错误或低效地触发JS,我也很乐意改变它。
答案 0 :(得分:0)
另外一个“我不知道为什么会这样”,但我尝试将FacesContext.getCurrentInstance().getExternalContext().redirect("/secure/AccountView.xhtml");
添加到stripeCCBean.update()方法的末尾,现在它可以正常工作。
仍然不知道为什么我必须通过commandButton的onclick调用update()方法,但是...
答案 1 :(得分:0)
请删除preventDefault函数,该函数会阻止浏览默认行为,您无需使用onclick事件。
答案 2 :(得分:0)
如果您想通过f:commandButton / f:commandLink导航,请使用action属性而不是onclick。
<f:commandButton ... action="#{myBean.actionHandler}".../>
如果您不使用任何ajax机制,则bean可以是@RequestScoped
,其他情况@ViewScoped
@Named
@RequestScoped
public class MyBean
{
public String actionHandler()
{
...
return "navigationRuleName";
}
...
}
navigationRuleName
中注册了faces-config.xml
:
<navigation-rule>
<from-view-id>/path/source.xhtml</from-view-id>
<navigation-case>
<from-outcome>navigationRuleName</from-outcome>
<to-view-id>/path/targetPageName.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
或者它可以是页面名称:
@Named
@RequestScoped
public class MyBean
{
public String actionHandler()
{
...
return "/path/targetPageName.xhtml?faces-redirect=true";
}
...
}
或者可以省略xhtml扩展名。
&#39;&#39;如果您希望在HTTP POST请求 - 响应导航后,浏览器的位置URL更改为targetPageName.xhtml,则需要实体或faces-redirect=true
参数。重定向通过HTTP POST后的另一个HTTP GET调用来完成。