我正試圖將Stripe整合到我的JSF應用程序中,並且難以從「添加信用卡」頁面導航。一切正常,除非用戶點擊提交後,頁面不會離開。使用h:commandButton和Javascript的JSF導航
下面是addCreditCard.xhtml facelet。將javascript邏輯添加爲提交eventListener並使用onclick="#{stripeCCBean.update()}"
觸發bean更新方法()是我可以使JavaScript成功創建令牌的唯一方法(如果由於某些未知原因導致javascript由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");
以及 FacesContext.getCurrentInstance().getExternalContext().dispatch("/secure/AccountView.xhtml");
到stripeCCBean.update()方法和既不作品。事實上,他們拋出了一個例外。
任何人都可以看到我做錯了什麼?如果我錯誤地或低效地觸發JS,我很樂意改變它。
在這個問題上我看到奇怪的事情:1.看不到你在哪裏從你的xhtml文件調用javascript。 2.如果這個bean是爲了管理一個視圖,使用'@ ViewScoped'而不是'@ RequestScoped'(否則你將失去從請求到請求的狀態)。 3.我寧願使用'f:viewAction'而不是'@ PostConstruct'註釋來調用'init'方法。 4.不要在JSF表單中使用'action'和'method'屬性。 JSF負責爲你自己計算它們。 5.'h:commandButton'的'onclick'屬性應該執行javascript listener,而不是服務器端方法。 –
@XtremeBiker,1)通過事件處理程序('document.querySelector('form'))觸發JS .addEventListener('submit',...')2)這個bean只是執行一些業務邏輯。它尚未完成,因此最終會將addCard(...)方法的結果保存到@SessionScoped bean中。 3)通過#2回答? 4)我無法獲得JSF表單來觸發javascript,將隱藏的字段保存到表單中,並調用沒有透傳表單屬性的bean方法。我不知道爲什麼,這是行不通的。 5)當JS被onclick觸發時,createToken()方法失敗,不知道爲什麼。 – Brooks
@XtremeBiker我意識到代碼有點到處都是,但僅僅是因爲我做不到'正確的方式'時才能使用。看起來像下面有一些建議,我會嘗試。 – Brooks