最近有一点忙,更新博客出现了一些延迟。由于在工作中使用了Apple Wallet,所以在结束后准备以此记录一下。希望后面有要做此功能的同学,能够有所启发,觉得有帮助的,记得点赞收藏关注
哦~
Apple Wallet 为我们提供了丰富的卡片场景,主要的卡片类型有以下几种:
简单来说,提供的这几种通行证(Apple Wallet 卡片)区分的类型只是为我们描述了具体的使用场景。我们可以根据实际需要来选择合适的卡片。
Add to Apple Wallet
图标来做触发,可点击查看官方参考文档Add Apple Wallet
图标后触发调用后端接口,拿到了 xxx.pkpass 的通行证,使用Safari 浏览器会自动识别,并在用户允许后添加至用户苹果设备的Apple Wallet中;Add Apple Wallet
图标后会显示一个二维码,用户通过使用原生相机扫描后可以跳转至Safari 浏览器触发后端接口,然后拿到通行证后添加至Apple Wallet;此步骤可以请公司内相关人员帮忙注册和生成,同时网上相关的资料应该很多,此文不加赘述;
通行证的创建从原理上来说很简单
Add to Apple Wallet
图标来支持Apple Walllet功能Add to Apple Wallet
图标后会调用这个接口来获取一个通行证文件;PKFileBasedSigningUtil
调用createSignedAndZippedPkPassArchive
方法创建通行证字节码数组;接下来我将用实际代码演示这个过程;
de.brendamour jpasskit 0.1.2
如果出现找不到类等情况,就看一下是否缺失某些依赖,因为jpasskit里面还关联了其他的一些依赖,但是目前项目中没有的;
String privateKeyPassword = "xxxx"; // the password you used to exportprivateKeyPath
String appleWWDRCA = PROJECT_PATH + "AppleWWDRCA.cer"; // this is apple's developer relation cert
String privateKeyPath = PROJECT_PATH + "passtype_key_dev.p12"; // the private key you exported from keychain
try {PKSigningInformation pkSigningInformation = PKSigningUtil.loadSigningInformationFromPKCS12FileAndIntermediateCertificateFile(privateKeyPath, privateKeyPassword, appleWWDRCA);
} catch (Exception e){
}
// 这里密码是你自己的证书密码public static final String PRIVATE_KEY_PASSWORD = "xxxx";public static final String APPLE_WWDRCA = "AppleWWDRCA.cer";public static final String PASSTYPE_KEY_DEV = "passtype_key_dev.p12";@PostConstructpublic void init() {try {appleWWDRCA = ClassUtils.getDefaultClassLoader().getResourceAsStream(APPLE_WWDRCA);privateKeyPath = ClassUtils.getDefaultClassLoader().getResourceAsStream(PASSTYPE_KEY_DEV);pkSigningInformation = new PKSigningInformationUtil().loadSigningInformationFromPKCS12AndIntermediateCertificate(privateKeyPath, PRIVATE_KEY_PASSWORD,appleWWDRCA);} catch (Exception e) {}}
如果是本地测试,直接用方法1,要发布到测试环境了可以用方法二来创建证书;
// 这里PKPass 是通行证对象,我们需要填充里面的内容;PKPass pass = new PKPass();// 固定设置1pass.setFormatVersion(1);// 这个字段用户apple wallet 更新,如果不需要Apple Wallet 更新可以去掉,目前只做创建也可以先删除调// pass.setWebServiceURL(new URL("https://xxxx:xxx/"));// 这个信息从Apple Developer中获取,对应替换成自己的pass.setPassTypeIdentifier("pass.com.xxx.xxx.xxx");// 这个信息从Apple Developer中获取, 对应替换成自己的// pass.setAuthenticationToken("xxxxxxxxxxxxxxxxx");String serialNumber = UUID.randomUUID().toString();// 这个是通行证唯一id,如果每次生成都是一个新的,那么可以每次重新New UUID。如果要去重,就需要保存这个ID,将它与业务id数据形成一对一关系;pass.setSerialNumber(serialNumber);// 来源于Apple Developerpass.setTeamIdentifier("xxxxxx"); // replace this with your team ID// 以下的信息都是通行证中的通用基本元素pass.setOrganizationName("Org name");pass.setBackgroundColor("#0F0242");pass.setForegroundColor("#FFFFFF");pass.setLabelColor("#FFFFFF");pass.setDescription("some description");pass.setLogoText("");pass.setExpirationDate(new Date());// 通行证中的二维码PKBarcode barcode = new PKBarcode();barcode.setFormat(PKBarcodeFormat.PKBarcodeFormatQR);barcode.setMessage("123456789");barcode.setMessageEncoding(Charset.forName("UTF-8"));pass.setBarcodes(Arrays.asList(barcode));// 这里的是通行证中的一些指定通行证的对应元素;如果不是使用PKStoreCard,替换成对应的就可以,然后根据自己预先设计的礼品卡样式来填充即可;PKStoreCard pkStoreCard = new PKStoreCard(); PKField pkField = new PKField();pkField.setKey("CardName");pkField.setValue("JD E-card");pkStoreCard.setPrimaryFields(ImmutableList.of(pkField));pass.setStoreCard(pkStoreCard);// 地理位置,可以不设置;PKLocation location = new PKLocation();location.setLatitude(xxxx); // replace with some latlocation.setLongitude(xxxx); // replace with some longList locations = new ArrayList();locations.add(location);pass.setLocations(locations);// 这里 isValid可以判断前面的元素是否都满足了,如果通过了就继续填充logo、图标等图形;if (pass.isValid()) {PKPassTemplateInMemory pkPassTemplateInMemory = new PKPassTemplateInMemory();// 这里PROJECT_PATH 替换为自己的项目文件路径pkPassTemplateInMemory.addFile(PKPassTemplateInMemory.PK_ICON, PROJECT_PATH + "images" + FILE_SEPARATOR + "icon.png");pkPassTemplateInMemory.addFile(PKPassTemplateInMemory.PK_ICON_RETINA, PROJECT_PATH + "images" + FILE_SEPARATOR + "icon@2x.png");pkPassTemplateInMemory.addFile(PKPassTemplateInMemory.PK_ICON_RETINAHD, PROJECT_PATH + "images" + FILE_SEPARATOR + "icon@3x.png");PKFileBasedSigningUtil pkSigningUtil = new PKFileBasedSigningUtil();// 传入通行证内容、通行证图片模板、和通行证安全证书,生成通行证文件byte[] 数据;byte[] signedAndZippedPkPassArchive = pkSigningUtil.createSignedAndZippedPkPassArchive(pass, pkPassTemplateInMemory, pkSigningInformation);
}
private void sendHttpResponse(HttpServletResponse httpServletResponse, ServletOutputStream out, byte[] signedAndZippedPkPassArchive) throws IOException {httpServletResponse.setHeader("Cache-Control", "no-cache,no-store");httpServletResponse.setHeader("Pragma", "no-cache");httpServletResponse.setHeader("Expires", "0");httpServletResponse.setHeader("Content-Disposition", "attachment; filename=\"pass.pkpass\"");httpServletResponse.setContentType("application/vnd.apple.pkpass");httpServletResponse.setContentLength(signedAndZippedPkPassArchive.length);httpServletResponse.setHeader("last-modified", String.valueOf(System.currentTimeMillis()));out.write(signedAndZippedPkPassArchive);out.flush();
}
pass.setWebServiceURL(new URL("https://xxxx:xxx/"));
获取最新通行证接口
先发送设备token给苹果APNS
,然后APNS会通知对应的设备来查询我们是否存在需要更新的通行证接口
,如果有的话,就会继续调用获取最新通行证接口
达到更新目的;回调服务器地址/v1/passes/{passTypeIdentifier}/{serialNumber}
,在这个接口中Apple 会传入要更新的通行证id,我们则根据通行证id查询到该通行证绑定的业务最新数据,并重新生成通行证(重复之前创建通行证的部分步骤)发送给Apple 设备 ;
import com.xxx.service.DemoService;
import com.xxx.service.RequestBodyUtils;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.ImmutableList;
import com.turo.pushy.apns.PushNotificationResponse;
import com.turo.pushy.apns.util.SimpleApnsPushNotification;
import com.turo.pushy.apns.util.concurrent.PushNotificationFuture;
import de.brendamour.jpasskit.apns.PKSendPushNotificationUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.HttpRequestHandlerServlet;
import springfox.documentation.annotations.ApiIgnore;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.util.Date;
import java.util.List;@RestController
@RequestMapping
@Api(tags = {"ceshi"})
public class DemoController {@Autowiredprivate DemoService demoService;@GetMapping("addWallet")@ResponseBody@ApiOperation("添加到苹果钱包中")public void addWallet(@ApiIgnore HttpRequestHandlerServlet httpRequest, @ApiIgnore HttpServletResponse httpResponse) {// 根据之前的创建通行证生成完善这个接口就可以;}/*** 注册设备以接收通行证的推送通知** @param deviceLibraryIdentifier 用于在未来请求中识别和验证此设备的唯一标识符。* @param passTypeIdentifier 通行证的类型,在通行证中指定。* @param serialNumber 通行证的序列号,在通行证中指定。* @param version 协议版本——当前为 v1。* 说明:此处可能用于将设备编号等信息记录;* header : 提供了 Authorization 标头;它的值是单词ApplePass,后跟一个空格,然后是通行证中指定的通行证的授权令牌。* 这里授权令牌看要不要进行保存;* 如果此设备的序列号已注册,则返回 HTTP 状态 200。* 如果注册成功,则返回 HTTP 状态 201。* 如果请求未授权,则返回 HTTP 状态 401。* 否则,返回适当的标准 HTTP 状态。* 这里可能要创建一个设备表,用于记录用户是否已经注册,如果注册成功则返回success*///Registering a Device to Receive Push Notifications for a Pass@PostMapping("v1/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}/{serialNumber}")@ApiOperation("注册设备")public ResponseEntity registrationWallet(@PathVariable("deviceLibraryIdentifier") String deviceLibraryIdentifier,@PathVariable("passTypeIdentifier") String passTypeIdentifier,@PathVariable("serialNumber") String serialNumber,@RequestBody PushToken pushToken) {// 这里的pushToken需要保存起来,然后推送更新时,将这个pushToken带上即可;System.err.println("time:"+ new Date());System.err.println("[暗余]. 注册设备");System.err.println("deviceLibraryIdentifier:"+deviceLibraryIdentifier);System.err.println("passTypeIdentifier:"+passTypeIdentifier);System.err.println("serialNumber:"+serialNumber);System.err.println("pushToken:"+ JSON.toJSONString(pushToken));return new ResponseEntity(HttpStatus.OK);}/*** 获取与设备关联的通行证的序列号** @param version 协议版本——当前为 v1。* @param deviceLibraryIdentifier 用于识别和验证设备的唯一标识符。* @param passTypeIdentifier 通行证的类型,在通行证中指定。* @param tag 来自先前请求的标签。(可选的)* 如果passesUpdatedSince参数存在,则仅返回自 指示的时间以来已更新的通行证tag。否则,返回所有通行证。* @return 如果有匹配的通行证,则返回 HTTP 状态 200 以及带有以下键和值的 JSON 字典:* lastUpdated(细绳)* 当前的修改标签。* * serialNumbers(字符串数组)* 匹配通行证的序列号。*
* 如果没有匹配的通行证,则返回 HTTP 状态 204。* 否则,返回适当的标准 HTTP 状态*/// Getting the Serial Numbers for Passes Associated with a Device@GetMapping("/v1/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}")@ResponseBody@ApiOperation("获取与设备关联的通行证的序列号")public ResponseEntity getSerialNumber(@PathVariable("deviceLibraryIdentifier") String deviceLibraryIdentifier,@PathVariable("passTypeIdentifier") String passTypeIdentifier,@RequestParam(value = "passesUpdatedSince",required = false) String passesUpdatedSince) {// 这里的pushToken需要保存起来,然后推送更新时,将这个pushToken带上即可;System.err.println("time:"+ new Date());System.err.println("[暗余]. 获取与设备关联的通行证的序列号");System.err.println("passTypeIdentifier:"+passTypeIdentifier);SerialNumberBo serialNumberBo = new SerialNumberBo();serialNumberBo.setLastUpdateTime("123455");serialNumberBo.setSerialNumbers(ImmutableList.of("aed1a60b-20fa-4346-8d26-43d8fe14f29d","123","456"));
// serialNumberBo.setSerialNumbers(ImmutableList.of("123"));return new ResponseEntity(serialNumberBo, HttpStatus.OK);}/**** passTypeId 凭证的类型,从服务器中指定* serial 服务器分配给凭证模板的序列号。您可以通过调用 xxx 来获取它;getPassbookTemplate*//*** 获取最新版本的通行证** @param version 协议版本——当前为 v1。* @param passTypeIdentifier 通行证的类型,在通行证中指定。* @param serialNumber 通行证中指定的唯一通行证标识符。* header: 提供了 Authorization 标头;它的值是单词ApplePass,后跟一个空格,然后是通行证中指定的通行证的授权令牌。* 如果请求被授权,则返回 HTTP 状态 200 以及传递数据的有效负载。* 如果请求未授权,则返回 HTTP 状态 401。* 否则,返回适当的标准 HTTP 状态。* 支持此端点上的标准 HTTP 缓存:检查If-Modified-Since标头,如果通道未更改,则返回 HTTP 状态代码 304。*/@GetMapping("/v1/passes/{passTypeIdentifier}/{serialNumber}")@ResponseBody@ApiOperation("获取最新版本的通行证")public void pushWallet(@PathVariable("passTypeIdentifier") String passTypeIdentifier,@PathVariable("serialNumber") String serialNumber,@ApiIgnore HttpServletResponse httpResponse) {// 这里的pushToken需要保存起来,然后推送更新时,将这个pushToken带上即可;System.err.println("time:"+ new Date());System.err.println("[暗余]. 获取最新版本的通行证");System.err.println("passTypeIdentifier:"+passTypeIdentifier);System.err.println("serialNumber:"+serialNumber);demoService.pushWallet(passTypeIdentifier, serialNumber, httpResponse);}/*** @param version 协议版本——当前为 v1。* @param deviceLibraryIdentifier 设备库标识符 用于识别和验证设备的唯一标识符。* @param passTypeIdentifier 通行证的类型,在通行证中指定。* @param serialNumber 通行证中指定的唯一通行证标识符。* header: 提供了 Authorization 标头;它的值是单词ApplePass,后跟一个空格,然后是通行证中指定的通行证的授权令牌。* 如果解除关联成功,则返回 HTTP 状态 200。* 如果请求未授权,则返回 HTTP 状态 401。* 否则,返回适当的标准 HTTP 状态。*
* 服务器解除指定设备与通行证的关联,当通行证发生变化时,不再向该设备发送推送通知。*/
// 注销设备@ResponseBody@DeleteMapping("/v1/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}/{serialNumber}")@ApiOperation("注销设备")public ResponseEntity unregisteringDevice(@PathVariable("version") String version,@PathVariable("deviceLibraryIdentifier") String deviceLibraryIdentifier,@PathVariable("passTypeIdentifier") String passTypeIdentifier,@PathVariable("serialNumber") String serialNumber) {// 这里的pushToken需要保存起来,然后推送更新时,将这个pushToken带上即可;System.err.println("time:"+ new Date());System.err.println("[暗余]. 注销设备");System.err.println("version:"+version);System.err.println("deviceLibraryIdentifier:"+deviceLibraryIdentifier);System.err.println("passTypeIdentifier:"+passTypeIdentifier);System.err.println("serialNumber:"+serialNumber);return new ResponseEntity(HttpStatus.OK);}/*** 记录错误日志* @param version 版本号* @param request 日志信息* @return 默认成功*/nseBody@RequestMapping(value = "/v1/log", method = RequestMethod.GET)public ResponseEntity log(@RequestBody LogContentRequest request) {if (Objects.nonNull(request)) {System.out.println(JSON.toJSONString(request));}return new ResponseEntity<>(HttpStatus.OK);}public static String getRequestBody() throws IOException {RequestAttributes attributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) attributes;HttpServletRequest request = servletRequestAttributes.getRequest();BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));//读取输入流的内容转换为String类型IOUtils必须引入org.apache.dubbo.common.utils.IOUtils;包String body = RequestBodyUtils.read(reader);return body;}@Data@NoArgsConstructor@AllArgsConstructorstatic class SerialNumberBo implements Serializable{private String lastUpdateTime;private List serialNumbers;}@Data@NoArgsConstructor@AllArgsConstructorstatic class LogContentRequest implements Serializable {private List logs;}@Data@NoArgsConstructor@AllArgsConstructorstatic class PushToken implements Serializable {private String pushToken;}// 主动推送通知public static void main(String[] args) throws IOException {String privateKeyPath = "D:\\xxx\\passtype_key_dev.p12"; // the private key you exported from keychainString privateKeyPassword = "xxxx "; // the password you used to exportprivateKeyPathString pushToken = "xxx";PKSendPushNotificationUtil pkSendPushNotificationUtil = new PKSendPushNotificationUtil(privateKeyPath, privateKeyPassword);PushNotificationFuture> simpleApnsPushNotificationPushNotificationResponsePushNotificationFuture =pkSendPushNotificationUtil.sendPushNotificationAsync(pushToken);SimpleApnsPushNotification pushNotification = simpleApnsPushNotificationPushNotificationResponsePushNotificationFuture.getPushNotification();System.err.println(JSON.toJSONString(pushNotification));}
}
这篇文章是博主实战得出的一些经验总结,如果有纰漏欢迎指出。写文不易,觉得不错的话麻烦点点关注鼓励一下哦,后续会继续更新原创精品内容!后面会继续讲Google Wallet 的部分,如果小伙伴谁遇到了问题可以留在评论区,我能解答的话会帮忙看一下;笔芯~
以下是我对Apple Wallet 的封装代码,有需要可供参考
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import de.brendamour.jpasskit.PKBarcode;
import de.brendamour.jpasskit.PKField;
import de.brendamour.jpasskit.PKPass;
import de.brendamour.jpasskit.enums.PKBarcodeFormat;
import de.brendamour.jpasskit.enums.PKDateStyle;
import de.brendamour.jpasskit.enums.PKTextAlignment;
import de.brendamour.jpasskit.passes.PKGenericPass;
import de.brendamour.jpasskit.signing.PKFileBasedSigningUtil;
import de.brendamour.jpasskit.signing.PKPassTemplateInMemory;
import de.brendamour.jpasskit.signing.PKSigningException;
import de.brendamour.jpasskit.signing.PKSigningInformation;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.InvalidParameterException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;/*** @author csdn 暗余*/
public class AppleWallet {private final PKPass pkPass;private final String serialNumber;private final PKGenericPass pkGenericPass;private final List headerFields;private final List primaryFields;private final List secondaryFields;private final List auxiliaryFields;private final List backFields;private PKSigningInformation pkSigningInformation;private final PKPassTemplateInMemory pkPassTemplateInMemory;private static final String VALID_ERROR = "The apple pass is NOT Valid!";public AppleWallet(String serialNumber) {pkPass = new PKPass();this.serialNumber = serialNumber;pkPass.setSerialNumber(serialNumber);pkGenericPass = new PKGenericPass();headerFields = Lists.newArrayList();primaryFields = Lists.newArrayList();secondaryFields = Lists.newArrayList();auxiliaryFields = Lists.newArrayList();backFields = Lists.newArrayList();pkPassTemplateInMemory = new PKPassTemplateInMemory();}public AppleWallet buildWhole(String backgroundColor, String foregroundColor, String labelColor, String description, String logoText) {pkPass.setBackgroundColor(backgroundColor);pkPass.setForegroundColor(foregroundColor);pkPass.setLabelColor(labelColor);pkPass.setDescription(description);pkPass.setLogoText(logoText);return this;}public AppleWallet buildQrCode(String message) {PKBarcode pkBarcode = new PKBarcode();pkBarcode.setFormat(PKBarcodeFormat.PKBarcodeFormatQR);pkBarcode.setMessage(message);pkBarcode.setMessageEncoding(StandardCharsets.UTF_8);pkPass.setBarcodes(ImmutableList.of(pkBarcode));return this;}public AppleWallet buildField(GenericPassEnum genericPassType, String keyField, String label, String value, PKDateStyle timeStyle, PKDateStyle dateStyle, PKTextAlignment pkTextAlignment) {if (Objects.nonNull(value) && value.length() > 0) {PKField pkField = new PKField();pkField.setKey(Optional.ofNullable(keyField).orElse(StringUtils.EMPTY));pkField.setLabel(Optional.ofNullable(label).orElse(StringUtils.EMPTY));pkField.setValue(value);pkField.setTimeStyle(timeStyle);pkField.setDateStyle(dateStyle);pkField.setTextAlignment(pkTextAlignment);addGenericList(genericPassType, pkField);}return this;}private void addGenericList(GenericPassEnum genericPassType, PKField pkField) {switch (genericPassType) {case HEADER:headerFields.add(pkField);break;case PRIMARY:primaryFields.add(pkField);break;case SECONDARY:secondaryFields.add(pkField);break;case AUXILIARY:auxiliaryFields.add(pkField);break;case BACK:backFields.add(pkField);break;default:throw new InvalidParameterException("Undefined type!");}}public AppleWallet buildField(GenericPassEnum genericPassType, String keyField, String label, Serializable value, PKTextAlignment pkTextAlignment) {PKField pkField = new PKField();pkField.setKey(Optional.ofNullable(keyField).orElse(StringUtils.EMPTY));pkField.setLabel(Optional.ofNullable(label).orElse(StringUtils.EMPTY));pkField.setValue(Optional.ofNullable(value).orElse(StringUtils.EMPTY));pkField.setTextAlignment(pkTextAlignment);addGenericList(genericPassType, pkField);return this;}public AppleWallet startGenericPass() {pkGenericPass.setHeaderFields(headerFields);pkGenericPass.setPrimaryFields(primaryFields);pkGenericPass.setSecondaryFields(secondaryFields);pkGenericPass.setAuxiliaryFields(auxiliaryFields);pkGenericPass.setBackFields(backFields);pkPass.setGeneric(pkGenericPass);return this;}public AppleWallet addFile(String pkPassTemplateInMemoryType, String url) throws IOException {int responseCode = 0;URL fileUrl = null;if (StringUtils.isNotEmpty(url)) {try {fileUrl = new URL(url);HttpURLConnection con = (HttpURLConnection) fileUrl.openConnection();responseCode = con.getResponseCode();} catch (Exception e) {responseCode = HttpStatus.NOT_FOUND.value();}}if (responseCode < HttpStatus.MULTIPLE_CHOICES.value() && responseCode >= HttpStatus.OK.value()) {pkPassTemplateInMemory.addFile(pkPassTemplateInMemoryType, Objects.requireNonNull(fileUrl));}return this;}public AppleWallet addFile(String pathInTemplate, InputStream inputStream) throws IOException {pkPassTemplateInMemory.addFile(pathInTemplate, inputStream);return this;}public PKPassTemplateInMemory getPKPassTemplateInMemory() {return pkPassTemplateInMemory;}public String getSerialNumber() {return serialNumber;}public byte[] create2Byte() throws PKSigningException {PKFileBasedSigningUtil pkSigningUtil = new PKFileBasedSigningUtil();return pkSigningUtil.createSignedAndZippedPkPassArchive(pkPass, pkPassTemplateInMemory, pkSigningInformation);}public AppleWallet buildWholeParent(String passTypeIdentifier, String authenticationToken, String teamIdentifier, String organizationName, PKSigningInformation pkSigningInformation) {this.pkSigningInformation = pkSigningInformation;pkPass.setPassTypeIdentifier(passTypeIdentifier);pkPass.setAuthenticationToken(authenticationToken);pkPass.setTeamIdentifier(teamIdentifier);pkPass.setOrganizationName(Optional.ofNullable(organizationName).orElse(StringUtils.EMPTY));return this;}public PKPass getPkPass() {return pkPass;}public AppleWallet isValid() {if (!pkPass.isValid()) {List validationErrors = this.pkPass.getValidationErrors();String error = CollectionUtils.isNotEmpty(validationErrors) ? validationErrors.get(0) : VALID_ERROR;throw new RuntimeException(error);} else {return this;}}public AppleWallet setWebServiceURL(String url) {try {pkPass.setWebServiceURL(new URL(url));pkPass.setFormatVersion(1);} catch (MalformedURLException e) {}return this;}public enum GenericPassEnum {HEADER, PRIMARY, SECONDARY, AUXILIARY, BACK, OTHER}
}