-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathatom.xml
1766 lines (1440 loc) · 402 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>船长的技术博客</title>
<link href="/atom.xml" rel="self"/>
<link href="http://troyyang.com/"/>
<updated>2024-12-26T13:30:32.277Z</updated>
<id>http://troyyang.com/</id>
<author>
<name>杨舟</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>JS实现一个复杂sticky效果</title>
<link href="http://troyyang.com/2024/12/25/javascript-implement-sticky-with-help-ai/"/>
<id>http://troyyang.com/2024/12/25/javascript-implement-sticky-with-help-ai/</id>
<published>2024-12-25T11:08:49.000Z</published>
<updated>2024-12-26T13:30:32.277Z</updated>
<content type="html"><![CDATA[<p>最近工作上遇到一个需要:一个产品列表的下, 当浏览器滚动到每个产品上时,需要将产品title sticky 到顶部,紧接着产品描述也sticky 到title 下部分,产品里的一个按钮则需要sticky 到底部,当产品滚动完成后,紧接着下一个产品的sticky。</p>
<p>看似很简单的需求,本以为通过css position: sticky 可以很快完成,没想到遇到了一茬接一茬的问题:</p>
<ol>
<li>首先每一个sticky 的元素style 会变化,例如字体或者边框,这就需要知道什么时候是处于sticky 状态,然后css 的sticky 是没有这样的状态选择器 </li>
<li>还是利用css sticky,只是使用 IntersectionObserver 来检测是否处于sticky 状态,如果是,那就加一个class 应用 style 的变化</li>
<li>按着思路,做出来的有个新问题:当滚动到第一个产品底部,刚好第二个产品顶部时,sticky收缩回原始位置的时候,又导致父元素高度变化,此时又触发sticky 条件,导致触发sticky,然而,sticky生效时,父元素高度又变小,又触发sticky收缩。。。所以sticky 闪烁的问题出现了</li>
</ol>
<p>#2 中的 detectSticky 可以用IntersectionObserver 实现代码:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">detectSticky</span>(<span class="params">element, onPin, onUnpin</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> observer = <span class="keyword">new</span> IntersectionObserver(</span><br><span class="line"> <span class="function"><span class="keyword">function</span>(<span class="params">entries</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> entry = entries[<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">if</span> (entry.intersectionRatio < <span class="number">1</span>) {</span><br><span class="line"> onPin();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> onUnpin();</span><br><span class="line"> }</span><br><span class="line"> }.bind(<span class="keyword">this</span>),</span><br><span class="line"> { <span class="attr">threshold</span>: [<span class="number">1</span>] } </span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> observer.observe(element);</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="params">()</span> =></span> observer.disconnect();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>如果要实现一个通用的任意元素的sticky 效果,可使用下面的方法:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createSticky</span>(<span class="params">element, position = <span class="string">'top'</span>, offsetY = <span class="number">0</span></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> isPinned = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">var</span> spacer = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> observer = <span class="keyword">new</span> IntersectionObserver(</span><br><span class="line"> (entries) => {</span><br><span class="line"> entries.forEach(<span class="function"><span class="params">entry</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (entry.isIntersecting) {</span><br><span class="line"> <span class="built_in">window</span>.addEventListener(<span class="string">'scroll'</span>, checkSticky);</span><br><span class="line"> checkSticky();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">window</span>.removeEventListener(<span class="string">'scroll'</span>, checkSticky);</span><br><span class="line"> <span class="keyword">if</span> (isPinned) unpin();</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> },</span><br><span class="line"> { <span class="attr">threshold</span>: [<span class="number">0</span>], <span class="attr">rootMargin</span>: <span class="string">'0px'</span> }</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">createSpacer</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> height = element.offsetHeight;</span><br><span class="line"> spacer = <span class="built_in">document</span>.createElement(<span class="string">'div'</span>);</span><br><span class="line"> spacer.className = <span class="string">'sticky-spacer'</span>;</span><br><span class="line"> spacer.style.height = height + <span class="string">'px'</span>;</span><br><span class="line"> spacer.style.opacity = <span class="number">0</span>;</span><br><span class="line"> element.parentNode.insertBefore(spacer, element);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">pin</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (!spacer) createSpacer();</span><br><span class="line"> element.classList.add(<span class="string">'is-sticky'</span>);</span><br><span class="line"> isPinned = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">unpin</span>(<span class="params"></span>) </span>{</span><br><span class="line"> element.classList.remove(<span class="string">'is-sticky'</span>);</span><br><span class="line"> <span class="keyword">if</span> (spacer) {</span><br><span class="line"> spacer.remove();</span><br><span class="line"> spacer = <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> isPinned = <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">checkSticky</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (!element.parentElement) {</span><br><span class="line"> unpin();</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> rect = element.parentElement.getBoundingClientRect();</span><br><span class="line"> <span class="keyword">var</span> shouldPin = position === <span class="string">'top'</span></span><br><span class="line"> ? rect.top < -offsetY && rect.bottom > offsetY</span><br><span class="line"> : rect.bottom > <span class="built_in">window</span>.innerHeight + offsetY && rect.top < <span class="built_in">window</span>.innerHeight;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (shouldPin && !isPinned) pin();</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (!shouldPin && isPinned) unpin();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> observer.observe(element.parentElement);</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="params">()</span> =></span> observer.disconnect();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>最终方案还有一个可改进的地方,由于 title 是触顶触发sticky,如果title 内容本身很高,spacer 的空白会比较明显,可以改为title 底部触顶触发, 只需要把 rect.bottom > offsetY 改为 rect.bottom > 0</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect/element-box-diagram.png" alt="image"></p>
<p>最后,平平无奇的一篇技术小点为何要写呢?最主要的是上面的最终成品是我在 claude.ai 帮助下完成,包括生成demo,重构代码,不得不感叹于 AI 真的改变程序生活</p>
]]></content>
<summary type="html">
<p>最近工作上遇到一个需要:一个产品列表的下, 当浏览器滚动到每个产品上时,需要将产品title sticky 到顶部,紧接着产品描述也sticky 到title 下部分,产品里的一个按钮则需要sticky 到底部,当产品滚动完成后,紧接着下一个产品的sticky。</p>
<
</summary>
<category term="web" scheme="http://troyyang.com/categories/web/"/>
<category term="ai" scheme="http://troyyang.com/tags/ai/"/>
</entry>
<entry>
<title>Indie hacker 新感</title>
<link href="http://troyyang.com/2024/01/27/indie-hacker-wordpress/"/>
<id>http://troyyang.com/2024/01/27/indie-hacker-wordpress/</id>
<published>2024-01-27T11:08:49.000Z</published>
<updated>2024-12-26T13:30:32.277Z</updated>
<content type="html"><![CDATA[<hr>
<h2 id="Indie-hacker"><a href="#Indie-hacker" class="headerlink" title="Indie hacker"></a>Indie hacker</h2><p>如果经常逛推特的话,可能常常听到Indie Hacker 这个词,可别认为是什么印度黑客呢,原意是指独立开发者,指一个人或者两三个人的小团队包干全部的开发及运营软件产品。</p>
<p>@tibo_maker (有点传奇,一个产品以一年一千万美金卖给了他高中同学,并且曾经四个月ship 了11 个产品)<br>@marc_louvion(四五个产品,能做到 50K+ 的MRR,重点推荐)<br>@levelsio (高产)<br>@luosays (国产出海,有些意见挺中肯)</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2024-01-27-indie-hacker.png" alt="image"></p>
<p>我个人应该也算Indie Hacker吧,虽然是兼职的,但也算是从产品构思,UX 设计,开发,到运营一条线走通,算一算现在手上发布了两个插件,并且都还有付费用户(一个$60 的MRR,另一个$20MRR )。虽说不能给自己带来多大的收入,但能把自己想法变现的同时,能体验不同的角色,并偶尔收到睡后收入的邮件,还是一件很美妙的事情。</p>
<h2 id="Stripe-Elementor"><a href="#Stripe-Elementor" class="headerlink" title="Stripe Elementor"></a>Stripe Elementor</h2><p>去年年中的时候发布了第二款 wordpress插件,算到现在,半年时间,收获到了7个付费用户和 50+ 到用户安装。相对于第一款插件,这一款我个人觉得更贴近用户,并且实用效果更明显,竞争力更强,开发和构思时间也更快捷的就完成了,这就是很大的进步,因为第一个插件实在投入了太多,坑也踩了太多。</p>
<p>举一些例子:1. 学习成本),因为是第一个软件,从着手准备到最后第一个版本,花了半年时间,学习了很多wordpress知识,license 平台管理,客户管理,ui设计等等等等,2. 技术选型),选择的是Reactjs 和 git submodules 来构建项目,原本的初心是充分利用react开发前端的灵活性,可事实证明我根本不需要如此灵活,也不需要考虑今后其他项目对这里的复用性,反而是丢失了wordpress 本身平台开发的便捷性。所以第二个插件,我果断选择了用jquery + tailwindcss 3. 产品构思),第一个插件的核心想法是提供各种支付的独立 UI组件,可事实证明,wordpress 绝大多数用户根本不需要这种支付组件,他需要的是比如woocommerce checkout 页面的stripe 集成,又或者是contact form 7 的stripe支付支持,这些才是用户支付的大部分story,正是基于这样,第二款插件就只是做了基于elementor form 的支付集成。事实证明,这一次播出去的种子,发芽成长的速度至少比第一款快了两倍!</p>
<h2 id="前五个用户是你的产品经理"><a href="#前五个用户是你的产品经理" class="headerlink" title="前五个用户是你的产品经理"></a>前五个用户是你的产品经理</h2><p>这句话在第二款插件里深深体会到了,还记得发布了两个月后,迎来了第一个付费用户,也是第一个挑战: 收到付费通知后开心不过一个小时,就又收到了退费通知,原因是安装pro 版本后,导致系统崩溃。即便很快修复了这个环境导致的问题,但早已无法挽回客户。对于退费,我并没有太过沮丧,因为前一个产品的退费请求也有好几次,但这次是记住了别被发布前的兴奋冲昏头脑,务必保证足够测试。</p>
<p>沉寂了一个月后,迎来了真正的来自法国的付费用户,客户是一个开发公司的总监,在使用插件过程中,出现了几处异常以及一个需求缺失,所以发邮件过来请求帮助。来回十几次邮件往复,和国庆加班,终于彻底解决了他的问题,但给我最大的体会是:用户的需求实在太切实际了,比如表单里到底是one-time支付还是subscription 应该由他们的客户选择,而非一开始设计form 的时候就定好,再比如提出了也提出了支持限定次数月份或者年的订阅,最后得以让我重新设计了一个更加合理清晰的配置选项,最有价值的恐怕还是提交流程的改变,这不仅需要我深入的看了Elementor的源码弄清楚内部流程,更基于这个改进了整个设计,我相信这是其他竞品很难做到的。</p>
<p>再往后几个月,又有几个付费用户相继提出了一些改进意见,都是非常中肯的,当然也有对插件的肯定</p>
<blockquote>
<p>I can’t thank you enough for your infinite patience and help. The plugin is great, super smooth and just what I need to integrate into our forms. </p>
</blockquote>
<p>偶然一次看推文,有人说到“前五个用户一定是你的产品经理”, 突然觉得太有道理了!一旦过了这个阶段,你应该足够对你的产品更有信心。</p>
<h2 id="Indie-Hacker"><a href="#Indie-Hacker" class="headerlink" title="Indie Hacker"></a>Indie Hacker</h2><p>最后,真心喜欢这么一个角色,让我在工作之余再也不会无所事事。</p>
]]></content>
<summary type="html">
前五个用户都是你的产品经理
</summary>
<category term="web" scheme="http://troyyang.com/categories/web/"/>
<category term="wordpress" scheme="http://troyyang.com/categories/web/wordpress/"/>
<category term="stripe" scheme="http://troyyang.com/tags/stripe/"/>
<category term="elementor" scheme="http://troyyang.com/tags/elementor/"/>
<category term="wordpress" scheme="http://troyyang.com/tags/wordpress/"/>
</entry>
<entry>
<title>wordpress 插件一年半赚$1500,还要坚持下去吗?</title>
<link href="http://troyyang.com/2022/11/27/stripe-express-summary/"/>
<id>http://troyyang.com/2022/11/27/stripe-express-summary/</id>
<published>2022-11-27T11:08:49.000Z</published>
<updated>2024-12-26T13:30:32.278Z</updated>
<content type="html"><![CDATA[<hr>
<p>今天统计了一下一年多前做的一个小插件,一共赚了$1500,二十个订阅用户,两百多有效免费用户。回顾之前的付出和现在时不时的维护,不禁在问自己,还要坚持做下去吗?</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2022-11-27-plugin-overview.png" alt="image"></p>
<h3 id="插件历程"><a href="#插件历程" class="headerlink" title="插件历程"></a>插件历程</h3><p>要说花费的时间,零零散散加起来还是挺多的,所有东西都是现学,从wordpess 是什么,wordpress 插件是什么,php 又是从零开始,再到如何管理插件,商业思维,UI设计,程序开发,客户沟通,官网设计,文档维护。。。实在花了不少心思,好在开出的花总会结果:</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2022-11-27-plugin-timeline.png" alt="image"></p>
<h3 id="后期维护"><a href="#后期维护" class="headerlink" title="后期维护"></a>后期维护</h3><p>除了第一版后的两三个月专心投入新功能开发后,接近有一年已经处于小功能开发和日常维护阶段,每周也花不了一两个小时在上面。如此下来,其实也能预见到插件的未来,已经达到了一个稳定甚至下降期。</p>
<p>但是事实证明,用户的增长真的和你的投入成正比,前段时间把一些很早就想加的功能加上,并且频繁发布版本后,直接改变了下降的颓势,所以相信你的付出,用户真的能看得到。</p>
<h3 id="我的客户"><a href="#我的客户" class="headerlink" title="我的客户"></a>我的客户</h3><ul>
<li>还记得第一个来自德国的神秘客户,是一个足球俱乐部,从没发邮件给我,直接购买,实在太惊讶无比,要知道当时wordpress.org公开的激活量是小于10的,而且初版程序不稳定,界面也丑陋</li>
<li>还有一位懂技术的客户,通过不停的邮件交流,帮我留下了难得的五星好评</li>
<li>无论免费还是付费,几乎都来自全球各地,有中途退款,取消订阅,也有特意给的NGO免费折扣,最重要的还是对客户忠诚</li>
</ul>
<h3 id="成本"><a href="#成本" class="headerlink" title="成本"></a>成本</h3><p>做产品一定要说成本: 最大的成本就是我的业余时间,其次才是每年三四十美元的服务器和域名费用。 如果要说回报的话,一两千美元的直接回报真的很少,但是我却能收获到的时候经验和教训,以及客户认可的喜悦。也许等到做下一个更大产品的时候,我能更少走弯路。</p>
<h3 id="未来"><a href="#未来" class="headerlink" title="未来"></a>未来</h3><p>只是最近继续开发维护的动力越来越小,一是连续好几个月没有新付费用户,二是竞争对手的降价策略或者新功能不停推陈出新,靠原有功能实在无法吸引用户,三是看着一堆的TODO list,以及发现前期的技术选型缺陷越来越大,很难做出大的改变。。。所以就此放弃了吗?对于我这种好强的人,肯定不会放弃的,那怕还有一个付费用户在续费,也不可能放弃辛苦建立的Brand,也许新开一个插件重新整理需求也是可以的。</p>
]]></content>
<summary type="html">
今天统计了一下一年多前做的一个小插件,一共赚了$1500,二十个订阅用户,两百多有效免费用户。回顾之前的付出和现在时不时的维护,不禁在问自己,还要坚持做下去吗?
</summary>
<category term="web" scheme="http://troyyang.com/categories/web/"/>
<category term="stripe" scheme="http://troyyang.com/tags/stripe/"/>
<category term="wordpress" scheme="http://troyyang.com/tags/wordpress/"/>
</entry>
<entry>
<title>如何通过 AWS certified solution architect associate (SAA)</title>
<link href="http://troyyang.com/2022/07/31/how-to-get-aws-certified-solutions-architect-associate/"/>
<id>http://troyyang.com/2022/07/31/how-to-get-aws-certified-solutions-architect-associate/</id>
<published>2022-07-31T11:08:49.000Z</published>
<updated>2024-12-26T13:30:32.277Z</updated>
<content type="html"><![CDATA[<hr>
<p>每天一两个小时的学习,前前后后三四个月,除了高考,也就这次考试的重视程度最高,最紧张了。回过头,看着网上评论有说小白一个月通过,我不太相信,即便对于我有一点AWS 基础的,考的过程还是很有压力。所以如果你也是准备在考的,请一定一定别低估考试的难度,务必准备足够充分。</p>
<h3 id="考证缘由"><a href="#考证缘由" class="headerlink" title="考证缘由"></a>考证缘由</h3><p>说起为何想着考这个证,主要有几个原因:1. AWS的情怀,在前面的三四年里,在一些个人项目里或多或少的用过一些AWS 服务,像EC2, Lamda, DynamoDB, Route53 等,也分享过一些AWS的文章<a href="https://troyyang.com/2018/05/12/aws_structure_series/">aws_structure_series</a>,所以想要继续提升aws的技能 2. 私下在和一位朋友的合作的过程中,发现国外的项目对AWS需求很大,如果能有个证,在项目上会有一定优势. 3. 个人的转型尝试 (也算是backup 吧)</p>
<h3 id="考试须知"><a href="#考试须知" class="headerlink" title="考试须知"></a>考试须知</h3><ul>
<li>报名费:$150,考试失败,需重新缴费补考</li>
<li>考试时间为 130 分钟 (划重点,对于咱们中国考生,可申请考试通融额外获取30分钟)</li>
<li>65 道,单选题或多选题 (15道不计分,多选题题目数不定)</li>
<li>Pearson VUE 和 PSI;考试中心或在线监考考试 (我是线上考的,但硬件和网络要求需考虑)</li>
<li>考试语言可选中英文,推荐选择中文,可在考试中来回切换中英文</li>
<li>SAA-C02试题会在8月29, 2022 过期,之后会采用C03 (所以我是看这个deadline 来安排我的考试)</li>
</ul>
<h3 id="备考历程"><a href="#备考历程" class="headerlink" title="备考历程"></a>备考历程</h3><p>从三四月决定考试开始,一开始便不知所措,除了官网的考试大纲和样本试题,国内的考试指南文章都实在帮助很小,大部分最后还是落在各种service 范围上,想要找到一个系统性的中文学习视频或文档实在很难,所以之后的所有重心都放在了国外的资源上,下面是我自个按照学习的时间线列出的资源:</p>
<ul>
<li>下载考试大纲和样本试题pdf <a href="https://aws.amazon.com/cn/certification/certified-solutions-architect-associate/?c=sec&sec=resources" target="_blank" rel="noopener">https://aws.amazon.com/cn/certification/certified-solutions-architect-associate/?c=sec&sec=resources</a></li>
<li><a href="https://www.youtube.com/watch?v=jypuayQpvao" target="_blank" rel="noopener">Youtube 小哥如何通过考试</a> 十几分钟,但非常励志,由其是SET A DEADLINE WITH A CONSEQUENCE </li>
<li>AWS 官方学习课程 <a href="https://explore.skillbuilder.aws/learn/course/external/view/elearning/1851/aws-technical-essentials?saa=sec&sec=prep" target="_blank" rel="noopener">AWS Technical Essentials</a>(5小时) 非常棒的介绍和引导,免费 </li>
<li><a href="https://www.youtube.com/watch?v=Ia-UEYYR44s&t=1175s" target="_blank" rel="noopener">Youtube AWS full course by FreeCodeCamp.org</a> 10小时,非常全面且免费,但缺少hands on</li>
<li><a href="https://www.udemy.com/course/aws-certified-solutions-architect-associate-saa-c02/" target="_blank" rel="noopener">视频教程 Stephane Maarek SAA by udemy.com</a> ~28小时,我的学习主力,需购买大约$15,请注意这是C02,后面应该买C03了</li>
<li><a href="https://jayendrapatil.com/" target="_blank" rel="noopener">jayendrapatil.com</a> 必须要提这人的博客,几乎所有备考的人都提到了他,包括了几乎所有考点,但文章真的太多,我大部分review 过,只是用来总结自己是否掌握过相关知识点,但推荐每篇都仔细看。</li>
<li><a href="https://www.udemy.com/course/practice-exams-aws-certified-solutions-architect-associate" target="_blank" rel="noopener">Practice Exams</a> 需购买,大约$20,6套试题,但感觉这些难度有点高,考了四套,都才50% 到 60%,建议换其他如<a href="https://www.whizlabs.com/aws-solutions-architect-associate/" target="_blank" rel="noopener">whizlabs</a></li>
<li>官方白皮书的介绍以及FAQ</li>
</ul>
<p>最最重要的,可能就是hands on了,务必动手多练, 比如 VPC,EBS+ASG, S3 等等,一是光看大部分肯定都记不住并且连不起来,二是真的就只是为了拿证了,实在不是个合格的云架构师,一些看似很简单的设计比如两层架构,一上手才会知道自己会漏多少。</p>
<h3 id="报考流程"><a href="#报考流程" class="headerlink" title="报考流程"></a>报考流程</h3><p>登录 <a href="https://www.aws.training/" target="_blank" rel="noopener">https://www.aws.training/</a> 后,最后被导航至 <a href="https://www.certmetrics.com/" target="_blank" rel="noopener">https://www.certmetrics.com/</a> ,填写好个人信息后(姓名务必使用拼音),就可以选择注册参考考试。(后期的成绩查询,证书下载都是在这个网站,PS:2021年11月后,证书已经更新,已经完全和网上看到的不一样了,黑底白字带验证码,实在不好看)</p>
<blockquote>
<p>可以申请考试通融延迟30分钟考试时间,如果选的是PSI, 可直接点击申请,基本申请就显示成功,之后的考试时间就会是160分钟</p>
</blockquote>
<h3 id="考试过程"><a href="#考试过程" class="headerlink" title="考试过程"></a>考试过程</h3><h4 id="考前准备"><a href="#考前准备" class="headerlink" title="考前准备"></a>考前准备</h4><ul>
<li>准备<strong>护照</strong>在手,(身份证不行,没有拼音和签名)</li>
<li>在线考试对自己的考场要求很严,需腾空考试房间,确保房间安静,房门关闭,中途不得上厕所,一旦有人或杂音,考官会终止考试</li>
</ul>
<blockquote>
<p>使用的PSI在线考试, 一直犹豫要不要选择Pearson VUE 考试中心现场考,但最终还是选择家里考,主要是因为当时PSI 有个考试优惠,以及疫情影响。如果家里网络稳定,以及对电脑有信心,最好是MAC,可以尝试在线考试。PSI 会在考试期间使用自家的浏览器,可锁定屏幕以及禁止其他软件运行,可提前下好,也可登录系统根据提示下载,下载地址在<a href="https://tca.psiexams.com/portal/testdelivery/sb_rpnow_download.jsp" target="_blank" rel="noopener">这里</a></p>
</blockquote>
<h4 id="考试中"><a href="#考试中" class="headerlink" title="考试中"></a>考试中</h4><p>选择的是9:30考,大概9点我就已经迫不及待的进入系统,按照操作,扫描护照鉴权通过后,考官会出现在对话框里,你看不见他,他能看见你,随后,他会有各种指示命令让显示各种视频角度等等检测(发现了我桌上的矿泉水都让给倒了),一切OK 会,考官会发题正式开始答题。</p>
<p>然后就是漫长的两个小时多的答题时间。。。原本的130分钟时间倒是差不多够,但由于我申请了通融,有多余的半个小时,花了十几分钟检查标记的题目。最后实在扛不住了,点击提交,再经过焦急的几秒后,终于显示通过。。。</p>
<h4 id="考试回顾"><a href="#考试回顾" class="headerlink" title="考试回顾"></a>考试回顾</h4><p>总体感觉很多架构题还挺有难度,简单的也有,大概20,30%,比如关于DDOS, XSS防范的服务是什么, 还记得其他比如</p>
<ul>
<li>Security group 相关的: web tier 和 database tier, 1433, 443</li>
<li>route53 多IP 策略(multi value)</li>
<li>VPC peering, private, public, nat</li>
<li>fraget, EKS</li>
<li>SQS, SNS</li>
<li>cloud front 的字段加密<br>上面都还只是不太难的,难的都记不住了</li>
</ul>
<h3 id="Next"><a href="#Next" class="headerlink" title="Next"></a>Next</h3><p>回过头,最大的收获不是获得这个证的荣誉,而是在面临工作压力,家庭琐事等等都情况下,坚持做一件有挑战的事,并做成后的成就感。其次,就是那个小哥说的SET A DEADLINE WITH A CONSEQUENCE, 为目标设置截止日,这个深有体会,之前曾设定6.1 日考试,可因为各种各样的原因一推再推,最后7月底才完成,所以没有破釜沉舟的决心是很容易懒惰下去并背离自己的预设目标。</p>
<p>证书有效期只有三年,所以这仅仅是一个开始,希望自己继续在云架构这条路上继续深造!</p>
<p>最后附一张常见VPC架构图收尾,你是否都知道?<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2022-07-31-aws-vpc-architect.png" alt> </p>
]]></content>
<summary type="html">
花了三四个月的业余时间,终于通过 AWS SAA 认证。回过头,最大的收获不是获得这个证的荣誉,而是在面临工作压力,家庭琐事等等都情况下,坚持做一件有挑战的事,并做成后的成就感。
</summary>
<category term="aws" scheme="http://troyyang.com/categories/aws/"/>
<category term="aws" scheme="http://troyyang.com/tags/aws/"/>
<category term="certification" scheme="http://troyyang.com/tags/certification/"/>
</entry>
<entry>
<title>一个人如何开发产品</title>
<link href="http://troyyang.com/2022/01/21/how-to-create-your-own-product-with-one-person/"/>
<id>http://troyyang.com/2022/01/21/how-to-create-your-own-product-with-one-person/</id>
<published>2022-01-21T11:08:49.000Z</published>
<updated>2024-12-26T13:30:32.277Z</updated>
<content type="html"><![CDATA[<hr>
<p>经过一年的打磨,现在产品的状态应该处于稳定上升期,收获100+来自全球各地的稳定用户,包括10+的付费用户,不算大的成功,但也能激励我继续投入, 相关的输出部分提现在下面的link 中:</p>
<ul>
<li><a href="https://itstripe.com/" target="_blank" rel="noopener">https://itstripe.com/</a></li>
<li><a href="https://docs.itstripe.com/" target="_blank" rel="noopener">https://docs.itstripe.com/</a></li>
<li><a href="https://wordpress.org/plugins/wp-stripe-express/" target="_blank" rel="noopener">https://wordpress.org/plugins/wp-stripe-express/</a></li>
</ul>
<h3 id="Idea"><a href="#Idea" class="headerlink" title="Idea"></a>Idea</h3><p>是什么促使你有做这个产品的想法,一定是某个痛点被暴露,无论是你自己遇到或者朋友,客户遇到,做这个有前途吗?收益值得做吗?这些应该是最初的那个想法应该带给你的第一个问题。以我做的这个为例,因为之前写的一篇关于stripe在中国的微信和支付宝支付的文章,被很多读者关注,也收到很多询问问题的邮件,本着能帮就帮的原则,绝大多数邮件也都仔细回复,甚至有些还被添加了微信,在和好多用户的沟通中,发现好些都是期望用到wordpress 中去,于是开始了对WP 的了解,之前懂一点WP,知道很多人在用,可没想到居然多年过去,还是这么火爆,维基百科显示超过全球40%的网站都是由它搭建,并且还有上涨的趋势(大火的 wix 也只能占零头不到),实在惊呆,其中不乏很多厂商依靠他来售卖插件和主题,市场还是足够大。为何我不能做一款基于WP的免费+付费插件,定位于前期面向中国,后期更多海外国家支付,再想大一点,专做Stripe 集成服务,毕竟这几年对stripe的开发还是很熟悉,而国内对这部分的了解还是很少?</p>
<h3 id="前期调研"><a href="#前期调研" class="headerlink" title="前期调研"></a>前期调研</h3><p>找出竞争者,看下当下的市场情况,能做到多大, 能比竞争者做的更好吗,甚至技术的调研。经过一段时间的调查,列出了一个100%匹配的竞争者以及几个间接竞争者。从调研的角度看,这个产品值得做,为什么,因为世面上已经有,就代表有市场,不用担心做的东西没人用,这倒是省去了一大顾虑,接下来的考虑就是如何超过他们。从产品角度而言,对于这个直接竞争者,我有90%的把握比他做的更好,既然他能买出产品,那为什么我不能?所以后面定的第一个短期目标就是超过他,后面从自己的客户信息来看,似乎也的确也做到了。但针对中国支付,这个市场的确有点小,估计一年最多几十个付费用户,所以产品也不能单一,可以扩展到海外其他国家,这些就是这几个间接竞争者,市场够大(做的最好的一年有上万个的客户),竞争也必然更加激烈,但也是迟早需要面对的。</p>
<h3 id="合伙人"><a href="#合伙人" class="headerlink" title="合伙人"></a>合伙人</h3><p>之所以想找合伙人,初衷是自己之前也独立做过一些好玩的东西,知道单打独斗的困难,不是因为事情繁杂,而是孤独,以及孤独带来的惰性,希望两个人的话,能相互互补,互相搀扶。后来也找到了一个在德国的好朋友,可惜没多久因为他忙于学业,最终还是落到了一个人头上,经过这短暂的合作,深深的明白这句“一个人可以让你走的更快,一个团队可以让你走的更远”。个人的建议是,如果是产品初期,并且自己属于产品控以及强势的一方,最好还是一个人做,会省掉你很多时间和精力,不用去想沟通的成本,后期如何合作运营,想法的冲突和利益的分配等等,一个人会让项目推进的更快更符合自己的初衷,当然事情会更多更杂。但是后期,等到产品雏形一出,甚至投入市场,则需要更多更专业的投入和设计,此时拿着产出去找投资或者合伙人不是更有信心吗?</p>
<h3 id="总体规划"><a href="#总体规划" class="headerlink" title="总体规划"></a>总体规划</h3><p>列了个之前做的计划图,实际的计划远不止这个图<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2021-1-20-stripe-express-plugin-plan.png" alt></p>
<h3 id="产品研发"><a href="#产品研发" class="headerlink" title="产品研发"></a>产品研发</h3><p>想做一个专业的针对国外的软件产品,不是简单的前端加后端,再套个UI 就完事了,至少下面的项目是需要考虑的:</p>
<h4 id="官网-https-itstripe-com"><a href="#官网-https-itstripe-com" class="headerlink" title="官网 (https://itstripe.com)"></a>官网 (<a href="https://itstripe.com" target="_blank" rel="noopener">https://itstripe.com</a>)</h4><p>(主域名,Logo, 企业邮箱,技术支持, 产品介绍,演示(Demo),价格定位,各种条约:隐私,授权,退款条约等等)</p>
<p>门面必须要有,并且一定要专业!其中,主域名一定要想好,后期所有推广,品牌都和这个息息相关,不能后面随便修改,官网的实现是基于gatsby JS,然后自己找了个好看点的主题魔改,虽然还是很丑(一位付费用户抱怨这网站看起来不专业),但还能将就用,最后托管到gitlab 的page 上,后端就一个AWS lambda 提供API 来发送contact me的邮件。</p>
<p>企业邮箱可以使用阿里云的企业邮箱服务,5年免费,可以薅一下,有企业邮箱会让你的网站更加专业,客户会更加信任,什么<a href="mailto:[email protected]" target="_blank" rel="noopener">[email protected]</a>, <a href="mailto:[email protected]" target="_blank" rel="noopener">[email protected]</a> 等等等等,虽然都是我一个人在打理,哈哈</p>
<p>条约,一定要重视,从google analysis 上看,神奇的是,如此偏门的页面居然也有不少人会去点击,让我不得不重视其中的每一条每一款。 </p>
<h4 id="产品帮助文档-https-docs-itstripe-com"><a href="#产品帮助文档-https-docs-itstripe-com" class="headerlink" title="产品帮助文档(https://docs.itstripe.com)"></a>产品帮助文档(<a href="https://docs.itstripe.com" target="_blank" rel="noopener">https://docs.itstripe.com</a>)</h4><p>产品当然得有帮助文档,要不然客户每次来烦你,你的邮件一定会爆的,什么quick start guide, setting, Q&A 放文档上就好了,节约你和客户的大把时间。 </p>
<h4 id="产品-Demo-https-itstripe-com-demo-https-demo-itstripe-com"><a href="#产品-Demo-https-itstripe-com-demo-https-demo-itstripe-com" class="headerlink" title="产品 Demo (https://itstripe.com/demo, https://demo.itstripe.com)"></a>产品 Demo (<a href="https://itstripe.com/demo" target="_blank" rel="noopener">https://itstripe.com/demo</a>, <a href="https://demo.itstripe.com" target="_blank" rel="noopener">https://demo.itstripe.com</a>)</h4><p>当然也少不了了,哪怕是静态的也好,这是目标客户点击率最高的地方。</p>
<h4 id="产品研发-https-wordpress-org-plugins-wp-stripe-express"><a href="#产品研发-https-wordpress-org-plugins-wp-stripe-express" class="headerlink" title="产品研发 (https://wordpress.org/plugins/wp-stripe-express/)"></a>产品研发 (<a href="https://wordpress.org/plugins/wp-stripe-express/" target="_blank" rel="noopener">https://wordpress.org/plugins/wp-stripe-express/</a>)</h4><p>做的WP插件,需要考虑,如何安装,激活,如何区分付费和免费用户,如何升级付费用户,客户如何支付,如何管理License(是否过期等)</p>
<p>自学了php,wordpress API, 外加 4 个 react repo(官网,文档,插件后端,插件前端) ,对于如何集成付费用户,又使用到了freemius 来管理代码和客户,为了产品的介绍,又摸索学习了Figma 设计图形,logo </p>
<h4 id="运营"><a href="#运营" class="headerlink" title="运营"></a>运营</h4><p>保证优质的回复每一封客户的邮件,遵守退款条约(退了两三次,好心痛),从客户需求里或者自己挖掘新功能(千万别模仿竞争者,保持产品的独立性),保持定期产品更新,让目标客户或者现有客户知道这产品的活力,WP 上不少插件都是没人维护状态。</p>
<p>下面是我指定的 TODO 运维图,有些是客户提的需求,一些来自自己发现需要优化的地方。<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2021-1-20-stripe-express-plugin-todos.png" alt></p>
<h4 id="客户的-feedback-是你产品最大的财富"><a href="#客户的-feedback-是你产品最大的财富" class="headerlink" title="客户的 feedback 是你产品最大的财富"></a>客户的 feedback 是你产品最大的财富</h4><p>下面是最近来自德国一位付费用户的邮件回复,真的深深的让我体会到,你的产品远没你想的那么好,但你的付出总会得到回报:</p>
<blockquote>
<p>So I’m happy about you being so responsive and willing to help, because looking at the low number of installations shown for your free version on Wordpress org, the at first not working feature for metadata, broken homepage and the failed payment and no imprint with physical company address on homepage did not make the very best impression at first. You have proved, that my first impression was not right, but I bet others that evaluate the best Stripe plugin might come to a similar conclusion. </p>
</blockquote>
<h3 id="思考"><a href="#思考" class="headerlink" title="思考"></a>思考</h3><p>这一年以来,还是花了太多的业余时间在这上面,包括多少个周末,甚至大理游玩都在弄,到现在也只是踏出了第一步, 列表里还列了很多关于这产品的TODO LIST,包括UI 的更新,大功能新增等,希望来年能完善好。</p>
<p>产品的故事实在太多,写着写着就停不下来,也许这就是折腾的意义,对提升个人知识的广度实在太多了</p>
]]></content>
<summary type="html">
自己业余开发的stripe express 产品发布一年,收获100+来自全球各地的用户,包括10+的付费用户, 个人研发产品的路还在继续,记录下这一年的点滴。
</summary>
<category term="stripe" scheme="http://troyyang.com/categories/stripe/"/>
<category term="payment" scheme="http://troyyang.com/tags/payment/"/>
<category term="stripe" scheme="http://troyyang.com/tags/stripe/"/>
</entry>
<entry>
<title>2021年度回顾</title>
<link href="http://troyyang.com/2022/01/20/my-2021/"/>
<id>http://troyyang.com/2022/01/20/my-2021/</id>
<published>2022-01-20T11:08:49.000Z</published>
<updated>2024-12-26T13:30:32.278Z</updated>
<content type="html"><![CDATA[<hr>
<p>简单的回顾,2021,还是处于疫情阶段,经历的人和事也挺多:</p>
<ul>
<li>经历人生第一次公司大裁员(自己没被裁,可看着身边一半以上的人走人流,还是感慨万分,不是因为幸运,而是归属感和危机感深深刺激了自己)</li>
<li>自己业余开发的stripe express 产品发布一年,收获100+来自全球各地的用户,包括10+的付费用户</li>
<li>用近半年兼职为一日本朋友开发web系统,收获了除开发经理以外,产品经理和项目管理更多角色的担当</li>
</ul>
<h3 id="关于裁员"><a href="#关于裁员" class="headerlink" title="关于裁员"></a>关于裁员</h3><p>作为一家美企研发中心,面对美国严峻的疫情情况,公司收入锐减,不得已开启裁员模式,看着昔日熟悉的面孔,看着离开的人开开心心拿着N+2的package离开,其中还不乏拿着顶格12+2的同事,但又同时饱含泪水的拥抱离开。。。这个时候不禁在想,到底留下来的才是幸运儿还是离开的呢??(现在看必须是离开的,因为大多数人在一年后又重新回到公司)</p>
<p>这次裁员,最大的感受还是人不能过的太安稳,尤其在外企这种生活工作很balance的节奏,如果不规划好自己的未来,很容易温水煮青蛙。其次是安全感,安全感一定不是公司给的,即便你是处于全球500強,so what? 一定要有自己的一套技能和体系,只有这种安全感才会让你坦然面对常说的35或者40岁危机</p>
<h3 id="关于自己做产品"><a href="#关于自己做产品" class="headerlink" title="关于自己做产品"></a>关于自己做产品</h3><p>感想太多,单独写了另一篇 <a href="https://troyyang.com/2022/01/21/how-to-create-your-own-product-with-one-person/">一个人如何开发个人产品</a></p>
<h3 id="关于兼职项目"><a href="#关于兼职项目" class="headerlink" title="关于兼职项目"></a>关于兼职项目</h3><p>一个机缘巧合,开始为一来自日本的华人朋友开发系统,需求从原本最初计划的匹配工具发展到日本广为流行的案件人才管理系统。找了个朋友做后端,我就负责需求沟通,前端开发,测试以及Demo等,技术栈选择的是基于AWS 的 serveless 架构。所获得的不仅仅是收入上的提高,更是如何系统的从0到1,从需求到,开发、架构整个流程的系统化,由其在AWS 上的各种服务,以前如果只是小打小闹的自己使用,那么这次算是一次商业化应用的实践。</p>
<h3 id="2022-计划"><a href="#2022-计划" class="headerlink" title="2022 计划"></a>2022 计划</h3><p>不想写什么读多少本书,短期目标就先拿个 AWS SAA 证书吧</p>
]]></content>
<summary type="html">
简单的回顾,2021,还是处于疫情阶段,经历的人和事也挺多,所以记录一下。
</summary>
<category term="misc" scheme="http://troyyang.com/categories/misc/"/>
<category term="misc" scheme="http://troyyang.com/tags/misc/"/>
</entry>
<entry>
<title>中国用户如何免费激活Stripe?</title>
<link href="http://troyyang.com/2021/06/02/activate-stripe-in-china-for-free/"/>
<id>http://troyyang.com/2021/06/02/activate-stripe-in-china-for-free/</id>
<published>2021-06-02T11:08:49.000Z</published>
<updated>2024-12-26T13:30:32.276Z</updated>
<content type="html"><![CDATA[<h1 id="中国用户如何免费激活Stripe"><a href="#中国用户如何免费激活Stripe" class="headerlink" title="中国用户如何免费激活Stripe?"></a>中国用户如何免费激活Stripe?</h1><p>本文会介绍无需开设海外银行账号或者香港账号,并免费的通过注册激活Stripe账号并提现,亲测有效!</p>
<p>主要通过使用万里汇海外账号绑定激活,其中万里汇是蚂蚁金服旗下的产品,值得可靠。(不是给万里汇打广告哦)</p>
<h3 id="什么是Stripe"><a href="#什么是Stripe" class="headerlink" title="什么是Stripe"></a>什么是Stripe</h3><p>想象你是一个跨境电商,想要把产品卖到全球,却面临一个问题,商品标价$100,日本客户想直接支付日元,欧洲客户想支付欧元。。。你不可能要求客户说我只支持美元,请兑换后再支付吧?</p>
<p>所以如果你还不知道Stripe,那推荐你去了解下。作为和PayPal一样存在的支付巨头(现在市值 $950亿),在国外早已火得一塌糊涂,使用他作为支付平台的商家和网站数不甚数,消费者渗透率覆盖了全球135个国家。。。</p>
<p><a href></a></p>
<h3 id="中国商户,NO"><a href="#中国商户,NO" class="headerlink" title="中国商户,NO"></a>中国商户,NO</h3><p>遗憾的是,如果你是身在中国,那么是不能激活Stripe 账号的(不激活只是注册账号倒是可以,但是没法收款和体现,只能测试),可以查看现在商户支持的<a href="https://stripe.com/global" target="_blank" rel="noopener">40多个国家/地区列表</a>, 其中香港是可以的。所以你能看到,这个注册公司地址里是没办法选择中国🇨🇳的,That’s the problem!</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2021-06-02-stripe-reg-china-issue.png" alt></p>
<h3 id="如果解决?万里汇-或者-TransferWise"><a href="#如果解决?万里汇-或者-TransferWise" class="headerlink" title="如果解决?万里汇 或者 TransferWise"></a>如果解决?万里汇 或者 TransferWise</h3><p>问题的瓶颈在于stripe 激活的时候,需要提供你的商业信息以及银行信息,并且保证银行上的名字和账号里的个人姓名一致。但又由于商业信息的国家地区只有上面提到的,所以也就导致中国用户没办法激活。</p>
<p>解决办法的思路就是,使用万里汇注册个香港账号或者其他国家的银行账号(虽说是虚拟的,但和实际没区别),然后再根据账号的信息拿去Stripe激活,看似简单,但还有很多坑需要趟,且听我慢慢道来。</p>
<hr>
<h3 id="注册万里汇"><a href="#注册万里汇" class="headerlink" title="注册万里汇"></a>注册万里汇</h3><p>点击注册地址 <a href="https://portal.worldfirst.com.cn/register" target="_blank" rel="noopener">https://portal.worldfirst.com.cn/register</a>, </p>
<ol>
<li>然后按步骤选择支付网关, 这里可以按个人情况多选几个(最好把stripe 勾选上),虽然我也不知道有多大影响</li>
</ol>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2021-06-02-worldfirst-reg-gateway.png" alt></p>
<ol start="2">
<li><p>选择类型,可以是个人, 也可以是公司,这里我选的个人</p>
</li>
<li><p>填写基本信息,按部就班的填好就行,注册就算成功</p>
</li>
</ol>
<h3 id="认证账号"><a href="#认证账号" class="headerlink" title="认证账号"></a>认证账号</h3><p>注册好了,是非认证状态的,这时是不能创建海外账号,还需要提供相应的信息上传去验证(据说可以支付宝快捷验证,但是我没发现有,只能拍照上传)</p>
<p>认证一般会持续一两天验证,等着收邮件就好了,如果有问题,可以联系自己的万里汇客户经理(真是一对一服务啊,这点好)</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2021-06-02-wordfirst-reg-home-page.png" alt></p>
<h3 id="创建海外货币账户"><a href="#创建海外货币账户" class="headerlink" title="创建海外货币账户"></a>创建海外货币账户</h3><p>终于到了关键步骤了,这里可以创建多个账户,每个账户就像自己银行卡一样,有卡号,为了stripe 注册方便,我创建了一个香港账户和一个美国账户</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2021-06-02-wordfirst-account-detail.png" alt></p>
<p>到了这一步,恭喜你,你已经开通了海外账户!关键是这个银行信息对后面的Stripe激活是非常重要。</p>
<hr>
<h2 id="创建和激活-Stripe-账号"><a href="#创建和激活-Stripe-账号" class="headerlink" title="创建和激活 Stripe 账号"></a>创建和激活 Stripe 账号</h2><p>首先创建Stripe 账号,<a href="https://dashboard.stripe.com/register" target="_blank" rel="noopener">https://dashboard.stripe.com/register</a>, 国家/地区可以选择香港,创建好之后,登录进入主页面, 这个时候如果你暂时不想激活,是完全可以的,可以切换<strong>测试</strong>模式进行你的支付开发测试,<strong>测试</strong>模式基本和<strong>在线</strong>模式一模一样,除了测试的支付账号是假的以外 (PS, 我就是没激活使用了一年多,纯粹作为开发使用)</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2021-06-02-stripe-dashboard.png" alt></p>
<h3 id="激活Stripe-账户"><a href="#激活Stripe-账户" class="headerlink" title="激活Stripe 账户"></a>激活Stripe 账户</h3><p>点击 <strong>激活你的账户</strong> , 这里看起来有很多步骤,不用怕填错,后面都是可以跳回去改的。</p>
<p>公司结构:</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2021-06-02-stripe-activate-comany-info.png" alt></p>
<p>选择香港,或者其他上面货币账号国家,地址可以填银行账号地址,类型可以选个人(如果公司的话,据说stripe 会对你账号保护性或者服务更好),点击下一步</p>
<p>公司代表:必须是你自己在上述银行账号的姓名,否则可能会无法体现到你银行</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/stripe-activate-represent-name-info.png" alt></p>
<p>地址信息可以继续用银行地址,电话号码最好是用中国的,可以选CN, 填写自己号码,因为后面可能会用来用来登录短信验证之类的,身份证ID 这个我没记错的话,是随便找的一个ID(只要位数和格式对了就行) ☹️</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2021-06-02-stripe-activate-represent-info.png" alt></p>
<p>银行详情:选择在万里汇创建的银行信息就行</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2021-06-02-stripe-activate-bank-info.png" alt></p>
<p>然后一直填下去,保存</p>
<h3 id="激活成功了吗?"><a href="#激活成功了吗?" class="headerlink" title="激活成功了吗?"></a>激活成功了吗?</h3><p>上述没问题的话,确实激活成功了,你也可以切换到线上模式去收款了,但是却无法提现(转账)到你银行卡里,还有两个重要的未完成步骤警告:</p>
<p>身份信息不匹配(个人信息验证失败,当然了,ID 都是假的) 和 US Tax Form (美国税收表)</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2021-06-02-stripe-activate-ux-tax-sign.png" alt></p>
<p>两个问题一个一个解决:</p>
<ul>
<li>ID 不匹配,进入提示的配置,只需要上传自己的身份证正反面就好了,一天左右就验证成功(不知道如何验证的,可能是后台人工验证,保证姓名一致就行,所以一定上传自己真实的身份证就,地址用自己中国地址)</li>
<li>W-8 form,一定要选非美国居民(勾选No),然后点击提交,会被导航至表单填写页面,基本信息stripe 已经帮你预填了,只需要签上个人姓名就好了</li>
</ul>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2021-06-02-w-8-form.png" alt></p>
<p><a href="https://support.stripe.com/questions/documents-for-identity-and-home-address-verification#upload" target="_blank" rel="noopener">documents-for-identity-and-home-address-verification</a></p>
<p><a href="https://support.stripe.com/questions/w-8-forms-collected-by-stripe" target="_blank" rel="noopener">w-8-forms-collected-by-stripe</a></p>
<p>等着这两个错误完成后,这个账号就算真正激活完成,并可以完成提现(每天,并且非常快)。</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2021-06-02-stripe-payout.png" alt></p>
<hr>
<h3 id="万里汇提现到人民币-成功"><a href="#万里汇提现到人民币-成功" class="headerlink" title="万里汇提现到人民币(成功)"></a>万里汇提现到人民币(成功)</h3><p>现在万里汇香港账号已经有收到的港币了,但如果需要的话,需要转为人民币(可转到支付宝),但根据万里汇客户经理说明,这是需要提供相应凭据,也就是stripe 或者 paypal 上的支付记录,表明来历明确。提现过一次,因为金额不大,并且提供了paypay 的收款记录,所以成功提现。</p>
<p>特别提醒的是,这篇文章目的只是为激活stripe指南, 不为跨境转账成功负责,请酌情选择。</p>
<p>特此声明,本文禁止转载至除 troyyang.com, <a href="http://itstripe.com" target="_blank" rel="noopener">itstripe.com</a> 以外的网站</p>
<p>相关推荐文章:<br><a href="https://troyyang.com/2020/12/30/wordpress-stripe-express-released/">wordpress stripe插件,支持微信和支付宝</a><br><a href="https://troyyang.com/2018/01/21/stripe_guide_alipay/">stripe集成 微信和支付宝</a> </p>
]]></content>
<summary type="html">
<h1 id="中国用户如何免费激活Stripe"><a href="#中国用户如何免费激活Stripe" class="headerlink" title="中国用户如何免费激活Stripe?"></a>中国用户如何免费激活Stripe?</h1><p>本文会介绍无需开设海外银
</summary>
<category term="stripe" scheme="http://troyyang.com/categories/stripe/"/>
<category term="payment" scheme="http://troyyang.com/tags/payment/"/>
<category term="stripe" scheme="http://troyyang.com/tags/stripe/"/>
</entry>
<entry>
<title>Wordpress 插件 Stripe Express 发布啦!</title>
<link href="http://troyyang.com/2020/12/30/wordpress-stripe-express-released/"/>
<id>http://troyyang.com/2020/12/30/wordpress-stripe-express-released/</id>
<published>2020-12-30T19:49:22.000Z</published>
<updated>2024-12-26T13:30:32.278Z</updated>
<content type="html"><![CDATA[<hr>
<h2 id="Stripe-Express-是什么?"><a href="#Stripe-Express-是什么?" class="headerlink" title="Stripe Express 是什么?"></a>Stripe Express 是什么?</h2><p>简单来说,Stripe Express 是一款针对wordpress 平台,帮助你使用 stripe 快速,方便完成跨境支付的一款免费插件(扩展功能收费)。其中,包括多种已经创建好的支付组件,包括一次性支付(one-time),电子钱包(支付宝,微信,Apple Pay, Google Pay,下面重点会提到微信和支付宝),表单支付等等组件,需要提及的是,上面的组件都支持常见的各种信用卡,Master card, Visa, 等等等等,以及其他国家地区的主流支付方式比如 Bancontact, FPX, EPS, SEPA, Giropay, Sofort, iDeal。</p>
<p>所以,只有你有一个 Stripe 的账号,那么超过三十多个国家地区的客户都可以向你支付(对于微信和支付宝,你无需申请支付宝或者微信的商家账号,即可免费收款)。</p>
<p>问题: 什么人更需要这个组件?<br>回答: 现阶段,因为还没集成woocommence, 所以如果你没有一个完整的电子商务网站比如使用 woocommence搭建,只是一个简单的Wordpress 网站,但是你又有自己的产品或者服务需要销售,而你只是想你用户简单的点击购买,付款。</p>
<p><a href="https://wordpress.org/plugins/wp-stripe-express/" target="_blank" rel="noopener">wordpress 插件传送门</a></p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/stripe-express-org-preview.png" alt> </p>
<h2 id="itstripe-com"><a href="#itstripe-com" class="headerlink" title="itstripe.com"></a>itstripe.com</h2><p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/itstripe-preview.png" alt> </p>
<p>在我们国内,大部分人肯定知道Paypal,却不知道Stripe,更别说用过,当然也和Stripe 暂不支持中国商家的原因分不开。殊不知,国外Stripe普及程度远大于我们的想象,很多网站都会加上对Stripe 的支持,因为这意味着你的网站可以面向全世界超过30个主要国家的客户收费,包括中国!所以想要做跨境支付的话,Stripe 你必须要熟悉! </p>
<p>众所周知,Wordpress全世界超过40%的网站都是他创建的,而且现在也依旧火爆。再加上之前很多朋友都在咨询我关于 stripe 在wordpress上的问题,所以主要侧重点会是在 stripe express 这款插件上。其次,网站会包括产品介绍,以及插件的文档,还有和stripe 相关集成服务,如果你有集成这方面的需求的话,或者Web 的支付开发,可以联系我们。 </p>
<h2 id="Stripe,微信-和-支付宝"><a href="#Stripe,微信-和-支付宝" class="headerlink" title="Stripe,微信 和 支付宝"></a>Stripe,微信 和 支付宝</h2><p>这是一个最重要的原因之一促使我想要做这么一个东西。有这么一部分人:1. 小商家或者个人网站用户想要接入微信或者支付宝,方便国内用户收费,2. 国外的中小网站想要针对中国用户微信和支付宝收费。但是对于他们而言,由其老外,要想接入微信或者支付宝支付接口,门槛还是有点高,需要去申请商家账号</p>
<p>stripe 却在这方面有着天然优势,由于已经和alipay 和 wechat 达成协议,Stripe 完全可以实现上面的收费,其中stripe 会收取3.4% + $0.50每笔的服务费。 </p>
<p>之前写过一篇关于 <a href="https://troyyang.com/2018/01/21/stripe_guide_alipay/">stripe集成 微信和支付宝</a>的文章,反响挺大的,看到很多评论和转载,也收到很多咨询的邮件,但是之前的那篇从技术角度其实有点老了,我会另外抽时间重新写一个更通用的集成方式(已经在这款插件中实践了)</p>
<h2 id="后续"><a href="#后续" class="headerlink" title="后续"></a>后续</h2><p>回想这半年多的开发时间(包括插件和官网),白天正常上班,晚上继续开发,连周末都不想出门,像打鸡血一样的完善产品,终于迎来了发布的日子。无论这款插件将来如何,安装量怎样,有过这么一段为了某个目标而全力以赴的日子也是极好的!</p>
]]></content>
<summary type="html">
<hr>
<h2 id="Stripe-Express-是什么?"><a href="#Stripe-Express-是什么?" class="headerlink" title="Stripe Express 是什么?"></a>Stripe Express 是什么?</h2>
</summary>
<category term="web" scheme="http://troyyang.com/categories/web/"/>
<category term="stripe" scheme="http://troyyang.com/tags/stripe/"/>
<category term="wordpress" scheme="http://troyyang.com/tags/wordpress/"/>
</entry>
<entry>
<title>Mobx在项目中的实践 及 与Redux的比较</title>
<link href="http://troyyang.com/2020/12/20/mobx/"/>
<id>http://troyyang.com/2020/12/20/mobx/</id>
<published>2020-12-20T14:03:17.710Z</published>
<updated>2024-12-26T13:30:32.278Z</updated>
<content type="html"><![CDATA[<p>之前在公司FEE内部做过一次技术分享,主要关于Mobx在项目中的使用一年后的体验以及和Redux 的一些比较(因为我们项目之前的状态管理选型选择的是mobx,而其他项目组的同学选择主要是Redux或者还在纠结如何选)。</p>
<p>以下都是根据查询各种资料后的个人理解概览</p>
<h2 id="Mobx-Overview"><a href="#Mobx-Overview" class="headerlink" title="Mobx Overview"></a>Mobx Overview</h2><blockquote>
<p>Mobx looks like a properties tracking and reaction lib.<br>基础部分就省了,只说结论:Mobx 看起来是属性追踪及作出相应反应的库,和Redux 不一样的是,他的状态是mutable的。</p>
</blockquote>
<h3 id="Mobx-4-amp-5"><a href="#Mobx-4-amp-5" class="headerlink" title="Mobx 4 & 5"></a>Mobx 4 & 5</h3><ul>
<li>Mobx 4 Limitations (Observable)</li>
<li>Mobx 5 Proxy based (Only ES 6 Browser, no polyfill)</li>
</ul>
<h2 id="Mobx-amp-Third-Party-view-lib"><a href="#Mobx-amp-Third-Party-view-lib" class="headerlink" title="Mobx & Third-Party view lib"></a>Mobx & Third-Party view lib</h2><ul>
<li>mobx & mobx-react</li>
<li>redux & react-redux</li>
<li>mobx & mobx-arch & mobx-backbone 有吗???</li>
</ul>
<p>Mobx 是可以单独使用的,这点和Redux一样,可以不需要依赖于任何UI 库像,React, Vue,当然如果把他们结合到一起,那才能发挥出最大的作用,所以就理所应当的有mobx-react。</p>
<p>我们公司内部有个UI 库叫arch,很老的了,requirejs时代的,比react, vue, angular还早,没有响应式的更新,核心只有一个render 方法,所以其实可以通过Mobx 简单改造为响应式的,一旦外部属性发生变化,就会触发重新渲染,至于内部状态嘛,呵呵,不考虑了,反正这只是个例子。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">var { observable, autorun } = require("mobx");</span><br><span class="line">var Entity = require('xx/xxxx/entity');</span><br><span class="line"></span><br><span class="line">var todoStore = observable({</span><br><span class="line"> todos: [],</span><br><span class="line"> get completedCount() {</span><br><span class="line"> return this.todos.filter(todo => todo.completed).length</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">autorun(function () {</span><br><span class="line"> // For Backbone</span><br><span class="line"> this.xxxBackBoneComponent = new Entity({</span><br><span class="line"> model: todoStore.todos,</span><br><span class="line"> editable: true</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> // For Arch</span><br><span class="line"> active.render($html, () => {</span><br><span class="line"> this.xxArchComponent = arch.getComponent(xxx);</span><br><span class="line"> });</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">todoStore.todos[0] = {</span><br><span class="line"> title: "Take a walk",</span><br><span class="line"> completed: false</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<h2 id="Mobx-Store-Design"><a href="#Mobx-Store-Design" class="headerlink" title="Mobx Store Design"></a>Mobx Store Design</h2><ul>
<li><a href="https://mobx.js.org/defining-data-stores.html" target="_blank" rel="noopener">Offical guide on Store design</a></li>
<li><a href="https://medium.com/dailyjs/mobx-react-best-practices-17e01cec4140" target="_blank" rel="noopener">Best Practice</a></li>
<li>UI State & Domain State</li>
</ul>
<p>这是我觉得最难的部分,如何设计好Mobx的Store?官方给出的一个<a href="https://mobx.js.org/defining-data-stores.html" target="_blank" rel="noopener">guide</a> 是划分为Domain store 和 UI store。Domain store和Redux的one-single store 可不一样,这里是可以有多个的,像users, books, movies, orders 都可以是一个Domain Store, 至于UI store,暂时我们只是存储一些全局的属性。所以,我们的项目中Store的结构大致如下:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">stores</span><br><span class="line">--root.ts</span><br><span class="line">--domain</span><br><span class="line">----aaaStore.ts</span><br><span class="line">----bbbStore.ts</span><br><span class="line">--ui</span><br><span class="line">----application.ts</span><br></pre></td></tr></table></figure></p>
<p>root.ts初始化所有domain和ui store:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">export default class RootStore {</span><br><span class="line"> @observable</span><br><span class="line"> aaStore;</span><br><span class="line"></span><br><span class="line"> @observable</span><br><span class="line"> bbStore;</span><br><span class="line"></span><br><span class="line"> @observable</span><br><span class="line"> applicationUIStore;</span><br><span class="line"></span><br><span class="line"> constructor() {</span><br><span class="line"> // Domain Store Init</span><br><span class="line"> this.aaStore = new AStore(this);</span><br><span class="line"> this.bbStore = new BStore(this);</span><br><span class="line"> ...</span><br><span class="line"></span><br><span class="line"> // UI Store Init</span><br><span class="line"> this.applicationUIStore = new ApplicationUIStore(this);</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>但是在实际的问题中,我们发现大部分的状态其实都是本地UI状态,(也许有人说用setState啊,如果业务复杂,状态很多, 并且基本会依赖其他store,最好抽出来)所以,问题来了,这些ui store我们放在哪里呢?同时,我们需要把Container 组件里的状态隔离开来,为什么隔离,一是因为UT 不好写(因为有inject,所以在UT里需要写很多Provider),二是傻瓜组件更不容易出错,参考Redux的connect用法,我们得到下面的结构:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ContainerAComponent</span><br><span class="line">--ContainerAComponent.tsx</span><br><span class="line">--ContainerAComponentUIStore.ts</span><br></pre></td></tr></table></figure></p>
<p>ContainerAComponentUIStore.ts<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">export default class ContainerAComponentUIStore {</span><br><span class="line"> rootStore;</span><br><span class="line"> constructor(rootStore) {</span><br><span class="line"> this.rootStore = rootStore;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> @observable</span><br><span class="line"> addHoc = '';</span><br><span class="line"> </span><br><span class="line"> @action.bound</span><br><span class="line"> onAdhocChange = (addHocNewValue) => {</span><br><span class="line"> ....</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></p>
<p>ContainerAComponent.tsx<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">export class ContainerAComponent extends React.Component {</span><br><span class="line"> handleAdhocChange = (e) => {</span><br><span class="line"> this.props.onAdhocChange(e.target.value);</span><br><span class="line"> }</span><br><span class="line"> ...</span><br><span class="line">}</span><br><span class="line">export default connectComponentStore(ContainerAComponent, ContainerAComponentUIStore);</span><br></pre></td></tr></table></figure></p>
<p>这样,我们导出了两个组件,一个是ContainerAComponent,就是一个简单组件,我们可以通过传统传props的方式去测试组件核心内容,另一个是HOC组件,其实是不用测试的。</p>
<p>而至于connectComponentStore方法,就是一个很简单的HOC<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">export default (WrapperedComponent, ComponentStore) => {</span><br><span class="line"> @inject('appStore')</span><br><span class="line"> @observer</span><br><span class="line"> class Connect extends React.Component {</span><br><span class="line"> @observable componentStore;</span><br><span class="line"></span><br><span class="line"> constructor(props) {</span><br><span class="line"> super(props);</span><br><span class="line"> this.componentStore = new ComponentStore(props.appStore);</span><br><span class="line"> this.ref = createRef();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> static displayName = `${WrapperedComponent.displayName || WrapperedComponent.name}-withUIStore`</span><br><span class="line"></span><br><span class="line"> componentDidMount() {</span><br><span class="line"> this.componentStore.mapState(this.props);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> componentDidUpdate(preProps, preState) {</span><br><span class="line"> this.componentStore.mapState(this.props);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> render() {</span><br><span class="line"> return (<WrapperedComponent ref={this.ref} {...this.componentStore.toProps} {...this.props} />);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return Connect;</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p>
<p>我们业务里,绝大部分都是用到的这种本地UI Store + 简单组件组合这种方式,也许就是所谓的local state component (忘了哪里听到的了)</p>
<h3 id="Mobx-State-Tree(MST)"><a href="#Mobx-State-Tree(MST)" class="headerlink" title="Mobx-State-Tree(MST)"></a>Mobx-State-Tree(MST)</h3><p>也许MST在大型项目中使用是个很好的方式,但我们暂时还没有去尝试。</p>
<h3 id="Project-Structure"><a href="#Project-Structure" class="headerlink" title="Project Structure"></a>Project Structure</h3><p>下面是Mobx的一些项目组织结构参考资料:</p>
<p><a href="https://medium.com/@daniel.bischoff/how-to-structure-your-mobx-react-app-8fd6d9d821a4" target="_blank" rel="noopener">https://medium.com/@daniel.bischoff/how-to-structure-your-mobx-react-app-8fd6d9d821a4</a><br><a href="https://github.com/gothinkster/react-mobx-realworld-example-app" target="_blank" rel="noopener">https://github.com/gothinkster/react-mobx-realworld-example-app</a> </p>
<h2 id="Mobx-React-vs-Redux-React"><a href="#Mobx-React-vs-Redux-React" class="headerlink" title="Mobx-React vs Redux-React"></a>Mobx-React vs Redux-React</h2><p>个人简单的一些看法:</p>
<ul>
<li>Workflow</li>
<li>Freestyle vs Strict</li>
<li>OOP styles vs FP</li>
<li>Small vs Large</li>
<li>Time-traval problem (Resolved by MST)</li>
<li>Container components (Inject vs Connect)</li>
<li><a href="https://github.com/sitepoint-editors/redux-crud-example/tree/master/src" target="_blank" rel="noopener">redux-crud-example</a> & <a href="https://github.com/sitepoint-editors/mobx-crud-example/tree/master/src" target="_blank" rel="noopener">mobx-crud-example</a></li>
</ul>
<p><a href="https://medium.com/@cameronfletcher92/mobdux-combining-the-good-parts-of-mobx-and-redux-61bac90ee448" target="_blank" rel="noopener">https://medium.com/@cameronfletcher92/mobdux-combining-the-good-parts-of-mobx-and-redux-61bac90ee448</a><br><a href="https://www.sitepoint.com/redux-vs-mobx-which-is-best/" target="_blank" rel="noopener">https://www.sitepoint.com/redux-vs-mobx-which-is-best/</a></p>
<h3 id="Learning-Redux"><a href="#Learning-Redux" class="headerlink" title="Learning Redux"></a>Learning Redux</h3><p>在一些小项目中用过Redux, 不得不说,Redux的学习成本要比Mobx高得多,比如下面的点,<br>redux, reducer, action, container component, selectors(reselect), redux-thunk, normalizing, ducks, and more waiting…</p>
<h2 id="Others-links"><a href="#Others-links" class="headerlink" title="Others links"></a>Others links</h2><h3 id="Mobx-Best-Practice"><a href="#Mobx-Best-Practice" class="headerlink" title="Mobx-Best-Practice"></a><a href="https://medium.com/dailyjs/mobx-react-best-practices-17e01cec4140" target="_blank" rel="noopener">Mobx-Best-Practice</a></h3><h3 id="Decorator-ES7-TS-vs-no-decorators"><a href="#Decorator-ES7-TS-vs-no-decorators" class="headerlink" title="Decorator (ES7/TS) vs no-decorators"></a>Decorator (ES7/TS) vs no-decorators</h3><h2 id="End"><a href="#End" class="headerlink" title="End"></a>End</h2><p>如果你有更好Mobx使用的一些心得,欢迎交流!</p>
]]></content>
<summary type="html">
<p>之前在公司FEE内部做过一次技术分享,主要关于Mobx在项目中的使用一年后的体验以及和Redux 的一些比较(因为我们项目之前的状态管理选型选择的是mobx,而其他项目组的同学选择主要是Redux或者还在纠结如何选)。</p>
<p>以下都是根据查询各种资料后的个人理解概览
</summary>
<category term="web" scheme="http://troyyang.com/categories/web/"/>
<category term="js" scheme="http://troyyang.com/tags/js/"/>
<category term="react" scheme="http://troyyang.com/tags/react/"/>
</entry>
<entry>
<title>纯JS实现按多列排序</title>
<link href="http://troyyang.com/2020/05/03/multi-sort-implement-with-native-js/"/>
<id>http://troyyang.com/2020/05/03/multi-sort-implement-with-native-js/</id>
<published>2020-05-03T16:34:22.000Z</published>
<updated>2024-12-26T13:30:32.278Z</updated>
<content type="html"><![CDATA[<h3 id="重要的事情还是要说的"><a href="#重要的事情还是要说的" class="headerlink" title="重要的事情还是要说的"></a>重要的事情还是要说的</h3><p>项目里没引用 <strong>lodash</strong> (因为和 underscore.js 冲突)</p>
<h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3><p>数据结构类似这种:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">const testData = [</span><br><span class="line"> { name: '1', primary: true, startDate: '2018-01-01T08:00:00Z', endDate: '2018-05-01T08:00:00Z' },</span><br><span class="line"> { name: 'A', primary: true, startDate: '2018-02-01T08:00:00Z', endDate: '2018-06-01T08:00:00Z' },</span><br><span class="line"> { name: 'a', primary: true, startDate: '2019-02-01T08:00:00Z', endDate: '2019-05-01T08:00:00Z' },</span><br><span class="line"> { name: 'b', primary: false, startDate: '2019-02-01T08:00:00Z', endDate: '2019-02-01T08:00:00Z' },</span><br><span class="line">]</span><br></pre></td></tr></table></figure></p>
<p>最近项目中有大量的对排序的新需求,由其是按多列来排序, 新需求大致如下:</p>
<ul>
<li>Archived 为true的排列到最后,否则排最前面</li>
<li>然后,按照 StartDate 时间,如果最新,则排前面</li>
<li>然后,如果 StartDate 相同,则按照 EndDate 来排,</li>
<li>然后,如果 EndDate 也相同,则按照 name 的字母表的顺序排</li>
</ul>
<p>同时呢,之前项目中也有很多类似的需求:</p>
<ul>
<li>先按照 ModifiedDate 排,</li>
<li>如果相同,则按 name 字母表顺序</li>
</ul>
<p>或者</p>
<ul>
<li>Primary 为true 的排前面</li>
<li>如果Primary 相同, 按照 name 字母表排序</li>
</ul>
<p>还有更多的类似需求,我们项目里原来有个 Sort.js 的公共方法来处理这些排序,选取了其中最长的一个 (其实上面需求的每一个实现都和这个差不多)<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">const sortFlattenPrograms = (flattenPrograms) =></span><br><span class="line"> flattenPrograms.sort((a, b) => {</span><br><span class="line"> // first sort by archived: unarchived first</span><br><span class="line"> if(a.archived && !b.archived) {</span><br><span class="line"> return 1;</span><br><span class="line"> } else if(!a.archived && b.archived) {</span><br><span class="line"> return -1;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // sort by start date: latest first</span><br><span class="line"> let dateCompareResult = compareDateLatestFirst(a.startDate, b.startDate);</span><br><span class="line"> if(dateCompareResult !== 0) {</span><br><span class="line"> return dateCompareResult;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // sort by end date: latest first</span><br><span class="line"> dateCompareResult = compareDateLatestFirst(a.endDate, b.endDate);</span><br><span class="line"> if(dateCompareResult !== 0) {</span><br><span class="line"> return dateCompareResult;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // sort by program name - location name: alphabetically (ignore case)</span><br><span class="line"> const nameCompareResult = compareStringAlphabeticallyIgnoreCase(getProgramFullName(a), getProgramFullName(b));</span><br><span class="line"> if(nameCompareResult !== 0) {</span><br><span class="line"> return nameCompareResult;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return 0;</span><br><span class="line"> });</span><br></pre></td></tr></table></figure></p>
<p>是不是很长,很丑,而且这只是一个排序,还有很多这种和0比较,然后再比较,所以继续加下去肯定不可取,维护是个很大的问题,UT 也很难写,要是能抽出中间部分就好了???</p>
<h3 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h3><p>先贴代码,其实核心就是抽取上面的各种comparator, 并且采用链式的方式执行,这里使用reduce方法来取了个巧,其实,查看了lodash的实现后, 他们采用的是 while 实现。</p>
<p>注意排序的顺序,是按照从右到左,我想的是尽量和 <strong>functional programming</strong> 的方式来写,并且compose 方法在lodash 里也是这个顺序,如果想改为从左往右,只需要将 <strong>reduce</strong> 改为 <strong>reduceRight</strong> 即可<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * Sort by order list from right to left</span><br><span class="line"> * For example: we want to order by start date, if date equal, then order by end date, if equal, then name</span><br><span class="line"> * composeOrderBy([oderByName, orderByEndDate, orderByStartDate])</span><br><span class="line"> * @param {*} comparators</span><br><span class="line"> */</span><br><span class="line"> const composeOrderBy = (comparators) => {</span><br><span class="line"> const makeChainedComparator = (first, next) => {</span><br><span class="line"> return function (a, b) {</span><br><span class="line"> var result = first(a, b);</span><br><span class="line"> if(result !== 0) return result;</span><br><span class="line"> return next(a, b);</span><br><span class="line"> };</span><br><span class="line"> };</span><br><span class="line"> return comparators.reduce(function (chained, first) {</span><br><span class="line"> return makeChainedComparator(first, chained);</span><br><span class="line"> });</span><br><span class="line"> };</span><br></pre></td></tr></table></figure></p>
<p>所以,上面的需求可以简单改为下面,其实comparators 是一个我预先定义好的各种比较方法<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">// 预先定义的方法</span><br><span class="line">comparators = {</span><br><span class="line"> compareStringField: (field, ignoreCase = true)=> (a, b) => { ... },</span><br><span class="line"> compareBoolField: (field, trueFirst = true) => (a, b) => { ... },</span><br><span class="line"> compareDateLatestFirst: (field) => (a, b) => { ... },</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">data.sort(composeOrderBy([</span><br><span class="line"> comparators.compareNameIgnoreCase(),</span><br><span class="line"> comparators.compareDateLatestFirst('endDate'),</span><br><span class="line"> comparators.compareDateLatestFirst('startDate'),</span><br><span class="line"> comparators.compareBoolField('archived', false)</span><br><span class="line"> ]));</span><br><span class="line"></span><br><span class="line">data.sort(composeOrderBy([</span><br><span class="line"> comparators.compareNameIgnoreCase(),</span><br><span class="line"> comparators.compareDateLatestFirst('modifiedDate')</span><br><span class="line"> ]));</span><br></pre></td></tr></table></figure></p>
<p>最终还是需要用到 array的sort 方法,但由于这不是纯函数,所以保险的做法就是调用sort前,先在clone一下</p>
]]></content>
<summary type="html">
<h3 id="重要的事情还是要说的"><a href="#重要的事情还是要说的" class="headerlink" title="重要的事情还是要说的"></a>重要的事情还是要说的</h3><p>项目里没引用 <strong>lodash</strong> (因为和 un
</summary>
<category term="Web" scheme="http://troyyang.com/categories/Web/"/>
<category term="web前端" scheme="http://troyyang.com/tags/web%E5%89%8D%E7%AB%AF/"/>
<category term="js" scheme="http://troyyang.com/tags/js/"/>
</entry>
<entry>
<title>神奇的 ES6 继承执行顺序问题</title>
<link href="http://troyyang.com/2019/12/17/amazing-es6-exec-order-issue/"/>
<id>http://troyyang.com/2019/12/17/amazing-es6-exec-order-issue/</id>
<published>2019-12-17T14:03:17.710Z</published>
<updated>2024-12-26T13:30:32.276Z</updated>
<content type="html"><![CDATA[<p>刷推的时候无意间发现一位google 工程师发的一个感叹,感叹发现的一个神奇的JS 6继承顺序问题。。。</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2019-12-17-js6-class-order-miracle-tweeter.jpeg" alt></p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2019-12-17-js6-class-order-miracle.png" alt></p>
<p>仔细看了看,确实好神奇,于是好奇的看了看babel转换出的结果:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">var SuperClass = function SuperClass() {</span><br><span class="line"> _classCallCheck(this, SuperClass);</span><br><span class="line"></span><br><span class="line"> _defineProperty(this, "foo", function () {</span><br><span class="line"> return console.log('foo init in supper class');</span><br><span class="line"> }());</span><br><span class="line"></span><br><span class="line"> console.log('super construtor');</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">var WhatEver =</span><br><span class="line">/*#__PURE__*/</span><br><span class="line">function (_SuperClass) {</span><br><span class="line"> _inherits(WhatEver, _SuperClass);</span><br><span class="line"></span><br><span class="line"> function WhatEver() {</span><br><span class="line"> var _this;</span><br><span class="line"></span><br><span class="line"> _classCallCheck(this, WhatEver);</span><br><span class="line"></span><br><span class="line"> console.log('before sub class constructor');</span><br><span class="line"> _this = _possibleConstructorReturn(this, _getPrototypeOf(WhatEver).call(this));</span><br><span class="line"></span><br><span class="line"> _defineProperty(_assertThisInitialized(_this), "foo", function () {</span><br><span class="line"> return console.log('foo init in sub class');</span><br><span class="line"> }());</span><br><span class="line"></span><br><span class="line"> console.log('after sub class constructor');</span><br><span class="line"> return _this;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return WhatEver;</span><br><span class="line">}(SuperClass);</span><br><span class="line"></span><br><span class="line">new WhatEver();</span><br></pre></td></tr></table></figure>
<p>就和他猜测的一样:没有supper的时候,字段的初始化是早于构造函数执行的,有supper的时候,字段初始化是在构造函数里的super后执行!</p>
<p>个人看法是故意放super之后是为了能在字段里访问到父类的字段?</p>
]]></content>
<summary type="html">
<p>刷推的时候无意间发现一位google 工程师发的一个感叹,感叹发现的一个神奇的JS 6继承顺序问题。。。</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvM
</summary>
<category term="web" scheme="http://troyyang.com/categories/web/"/>
<category term="js" scheme="http://troyyang.com/tags/js/"/>
</entry>
<entry>
<title>使用NetlifyCMS在线编辑Github上的博客</title>
<link href="http://troyyang.com/2019/11/24/githug-netlify/"/>
<id>http://troyyang.com/2019/11/24/githug-netlify/</id>
<published>2019-11-24T04:08:35.524Z</published>
<updated>2024-12-26T13:30:32.277Z</updated>
<content type="html"><![CDATA[<h3 id="Netlify-CMS-介绍"><a href="#Netlify-CMS-介绍" class="headerlink" title="Netlify CMS 介绍"></a>Netlify CMS 介绍</h3><p>使用Netlify CMS我感觉有以下优点:</p>
<ul>
<li>无缝支持Hexo 等十几种主流静态网站生成器 的 <strong>文章后台管理*</strong> </li>
<li>可视化在线编辑、新增github 上的markdown</li>
<li>自带图片上传功能</li>
<li>自动部署</li>
</ul>
<p>支持列表:<br>Jekyll, GitBook,Hugo, Gatsby, Nuxt, Next, Gridsome, Zola,Hexo, Middleman, Jigsaw,Spike ,Wyam,Pelican,VuePress,Elmstatic,11ty,preact-cli</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2019-11-24-netlifycms-list.png" alt></p>
<h3 id="为什么使用它"><a href="#为什么使用它" class="headerlink" title="为什么使用它"></a>为什么使用它</h3><p>对于我的情况:使用Hexo 网站生成器,托管在github上 <a href="https://github.com/Troy-Yang/troy-yang.github.io" target="_blank" rel="noopener">https://github.com/Troy-Yang/troy-yang.github.io</a>,其中Source branch是存放markdown等生成前分支,Master branch存放的是生成后的静态文件分支。<br>对于以前,如果要写一篇文章,基本是在source 分支里,新增一个markdown文件(可github上在线添加或者本地新增然后push),然后自动触发github 上配置的travis 自动部署流程,整体感觉已经很不错了。现在配置上Netlify CMS后, 可视化的在线编辑以及图片管理更加方便,可以随时随地发文章。<br>可惜, Netlify有个致命缺点:<strong>需要翻墙访问</strong></p>
<h3 id="HEXO-NetlifyCMS-配置"><a href="#HEXO-NetlifyCMS-配置" class="headerlink" title="HEXO NetlifyCMS 配置"></a>HEXO NetlifyCMS 配置</h3><p>只需要在hexo 的source/ 目录下添加admin 目录,新增下面两个文件:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">config.yml</span><br><span class="line">index.html</span><br></pre></td></tr></table></figure>
<p>config.yml需要根据自己情况进行配置:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">backend:</span><br><span class="line"> name: git-gateway</span><br><span class="line"> branch: Source</span><br><span class="line"></span><br><span class="line"># This line should *not* be indented</span><br><span class="line">media_folder: "source/images/uploads" # Media files will be stored in the repo under images/uploads</span><br><span class="line">public_folder: "/images/uploads" # The src attribute for uploaded media will begin with /images/uploads</span><br><span class="line"></span><br><span class="line">collections:</span><br><span class="line"> - name: "blog" # Used in routes, e.g., /admin/collections/blog</span><br><span class="line"> label: "Post" # Used in the UI</span><br><span class="line"> folder: "source/_posts" # The path to the folder where the documents are stored</span><br><span class="line"> create: true # Allow users to create new documents in this collection</span><br><span class="line"> slug: "{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md</span><br><span class="line"> fields: # The fields for each document, usually in front matter</span><br><span class="line"> - {label: "Layout", name: "layout", widget: "hidden", default: "post"}</span><br><span class="line"> - {label: "Title", name: "title", widget: "string"}</span><br><span class="line"> - {label: "Publish Date", name: "date", widget: "datetime"}</span><br><span class="line"> - {label: "Tags", name: "tags", widget: "list", required: false}</span><br><span class="line"> - {label: "Categories", name: "categories", widget: "list", required: false}</span><br><span class="line"> - {label: "Photos", name: "photos", widget: "list", required: false}</span><br><span class="line"> - {label: "Excerpt", name: "excerpt", widget: "string", required: false}</span><br><span class="line"> - {label: "Body", name: "body", widget: "markdown"}</span><br><span class="line"> - {label: "Permalink", name: "permalink", widget: "string", required: false}</span><br><span class="line"> - {label: "Comments", name: "comments", widget: "boolean", default: true, required: false}</span><br></pre></td></tr></table></figure>
<p>index.html</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><!doctype html></span><br><span class="line"><html></span><br><span class="line"><head></span><br><span class="line"> <meta charset="utf-8" /></span><br><span class="line"> <meta name="viewport" content="width=device-width, initial-scale=1.0" /></span><br><span class="line"> <title>Content Manager</title></span><br><span class="line"></head></span><br><span class="line"><body></span><br><span class="line"> <!-- Include the script that builds the page and powers Netlify CMS --></span><br><span class="line"> <script src="https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"></script></span><br><span class="line"> <script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script></span><br><span class="line"></body></span><br><span class="line"></html></span><br></pre></td></tr></table></figure>
<h3 id="Netlify-端配置"><a href="#Netlify-端配置" class="headerlink" title="Netlify 端配置"></a>Netlify 端配置</h3><h4 id="创建-Netlify-website"><a href="#创建-Netlify-website" class="headerlink" title="创建 Netlify website"></a>创建 Netlify website</h4><p>注意:和大部分人的做法不同的是,我Deploy到的地方并不是托管在Netlify自己的平台上,而是github上,所以这里我选择部署的是Source分支,而不是Master,因为我只是想要Netlify去修改我的Source分支,然后触发Travis自动发布到Master分支。</p>
<p>但是我依旧需要填写Netlify的部署,因为Netlify会自动帮我创建域名为troyyang.netlify.com的网站,任何我Source分支上的修改也会触发这个网站的自动部署</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2019-11-24-create-netlify-website.png" alt></p>
<h4 id="开启Netlify-Identity-和-Git-Gateway"><a href="#开启Netlify-Identity-和-Git-Gateway" class="headerlink" title="开启Netlify Identity 和 Git Gateway"></a>开启Netlify Identity 和 Git Gateway</h4><p>在Setting 的 Identity选项下:</p>
<ol>
<li>Enable Identity service</li>
<li>External providers 新增github</li>
<li>Enable Git Gateway</li>
</ol>
<h3 id="发布测试"><a href="#发布测试" class="headerlink" title="发布测试"></a>发布测试</h3><p>打开 <a href="https://troyyang.netlify.com/admin/" target="_blank" rel="noopener">https://troyyang.netlify.com/admin/</a> 然后使用github Oauth登录即可看到:</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2019-11-24-netlify-home.png" alt></p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2019-11-24-netlify-create.png" alt></p>
<p>新增文章后, 你会发现github 上的source目录下的_post 目录的markdown 文件新增了,如果上传了图片,也会看到source目录下多了images/upload目录,同时<a href="https://troyyang.netlify.com" target="_blank" rel="noopener">https://troyyang.netlify.com</a> 和 <a href="https://troyyang.com">https://troyyang.com</a> 下也自动发布了新的文章, 两者都是因为Source分支里新增了文件导致的自动部署。</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2019-11-24-netlify-file-structure.png" alt><br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2019-11-24-troyyang.png" alt><br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2019-11-24-netlify.png" alt></p>
<h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3><p>当我尝试打开 <a href="https://troyyang.com/admin/">https://troyyang.com/admin/</a> 使用github Oauth登录时,结果报错,而<a href="https://troyyang.netlify.com/admin/则没有:" target="_blank" rel="noopener">https://troyyang.netlify.com/admin/则没有:</a><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Failed to load settings from /.netlify/identity</span><br></pre></td></tr></table></figure></p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/uploads/2019-11-24-netlify-admin-error.png" alt></p>
<p>我怀疑是因为<a href="https://troyyang.com是托管在github上,而不是netlify上导致的。" target="_blank" rel="noopener">https://troyyang.com是托管在github上,而不是netlify上导致的。</a></p>
<p>Enjoy!</p>
]]></content>
<summary type="html">
<h3 id="Netlify-CMS-介绍"><a href="#Netlify-CMS-介绍" class="headerlink" title="Netlify CMS 介绍"></a>Netlify CMS 介绍</h3><p>使用Netlify CMS我感觉有以下优点:
</summary>
<category term="web" scheme="http://troyyang.com/categories/web/"/>
<category term="github" scheme="http://troyyang.com/tags/github/"/>
<category term="hexo" scheme="http://troyyang.com/tags/hexo/"/>
<category term="netlify" scheme="http://troyyang.com/tags/netlify/"/>
</entry>
<entry>
<title>AWS系列之使用无服务器架构你的网站</title>
<link href="http://troyyang.com/2018/12/16/aws_serverless/"/>
<id>http://troyyang.com/2018/12/16/aws_serverless/</id>
<published>2018-12-16T09:37:22.000Z</published>
<updated>2024-12-26T13:30:32.277Z</updated>
<content type="html"><![CDATA[<hr>
<h3 id="Serverless-有什么用啊?"><a href="#Serverless-有什么用啊?" class="headerlink" title="Serverless 有什么用啊?"></a>Serverless 有什么用啊?</h3><p>Jason最近又出新想法了,想要做一个简单的用户管理系统,好的,没问题,不就是在服务器上安装数据库,部署好网站吗?可答案是no,他不是专业人员,我也不可能永远维护这个服务器,更重要的是服务器开着就要美刀啊,还不能停,怎么办?有没有可以不用服务器的网站,有啊,你自己的静态博客不就是只用到了s3或者github的静态页面托管吗?可是数据库呢,后台api呢?额,这个嘛。。。 </p>
<p>好了,成功引出话题,要知道这是21世纪的云时代,只有你想不到,没有做不到的,这不,AWS早就提出了<a href="https://aws.amazon.com/cn/serverless/?nc1=h_ls" target="_blank" rel="noopener">Serverless</a>解决方案:S3 + GateWay API + Lambda + DynamDB,其中举例的一个天气的app架构:</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-Lambda-WebApplications.png" alt="image"></p>
<p>其中s3做静态页面托管,用户触发点击事件,调用Gateway API提供到接口,接口映射到Lambda服务端接口,Lambda再负责去处理和数据库相关到操作。整个过程不需要服务器,而且费用是极低的,按量付费,可扩展性也很强,基本做到可配置化。说了这么多,还是得用过才知道好不好。</p>
<h3 id="实现思路"><a href="#实现思路" class="headerlink" title="实现思路"></a>实现思路</h3><ol>
<li>服务端RestFull: Node express 实现RestFull API</li>
<li>创建lambda并上传服务端代码</li>
<li>配置API Gateway映射到lambda函数</li>
<li>客户端实现: Bootstrap 实现登录 和 管理页面</li>
<li>修改客户端api接口地址并上传至S3</li>
</ol>
<p>其中,到第三步的时候我们就已经创建好了一个完整的无服务器的 Restfull API,剩下的就是客户端调用了,客户端调用这个就可以是五花八门的了,这也不是本篇文章的重点。</p>
<h3 id="简单-RestFull-服务端实现"><a href="#简单-RestFull-服务端实现" class="headerlink" title="简单 RestFull 服务端实现"></a>简单 RestFull 服务端实现</h3><p>服务端的实现和平时实现一个Node RestFull api的完全没有任何区别, 部分代码如下:<br>app.js<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">'use strict';</span><br><span class="line"></span><br><span class="line">const express = require('express');</span><br><span class="line">const app = express();</span><br><span class="line">const bodyParser = require('body-parser');</span><br><span class="line">const cors = require('cors');</span><br><span class="line"></span><br><span class="line">app.use(bodyParser.json());</span><br><span class="line">app.use(bodyParser.urlencoded({ extended: true }));</span><br><span class="line">app.use(cors());</span><br><span class="line"></span><br><span class="line">let contacts = require('./data');</span><br><span class="line"></span><br><span class="line">app.get('/api/contacts', (request, response) => {</span><br><span class="line"> if (!contacts) {</span><br><span class="line"> response.status(404).json({ message: 'No contacts found.' });</span><br><span class="line"> }</span><br><span class="line"> response.json(contacts);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">const hostname = 'localhost';</span><br><span class="line">const port = 3001;</span><br><span class="line">const server = app.listen(port, hostname, () => {</span><br><span class="line"> console.log(`Server running at http://${hostname}:${port}/`);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">module.exports = app</span><br></pre></td></tr></table></figure></p>
<p>这一步做完,确保所有接口都能通过访问 localhost:3001/api/contacts</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-restful-success.png" alt="image"></p>
<h3 id="aws-serverless-express"><a href="#aws-serverless-express" class="headerlink" title="aws-serverless-express"></a>aws-serverless-express</h3><p>要使得上面的服务端代码能在lambda中允许,只需借助 npm 包<a href="https://github.com/awslabs/aws-serverless-express" target="_blank" rel="noopener">aws-serverless-express</a></p>
<p>在目录下新增 lambda.js文件<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">// lambda.js</span><br><span class="line">'use strict'</span><br><span class="line">const awsServerlessExpress = require('aws-serverless-express')</span><br><span class="line">const app = require('./app')</span><br><span class="line">const server = awsServerlessExpress.createServer(app)</span><br><span class="line"></span><br><span class="line">exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context)</span><br></pre></td></tr></table></figure></p>
<p>这也是为什么我们要在 app.js最后一行exports的原因<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">module.exports = app</span><br></pre></td></tr></table></figure></p>
<p>此时,将所有文件包括node_module目录全部打包为.zip 文件为后面使用。</p>
<h3 id="创建-lambda"><a href="#创建-lambda" class="headerlink" title="创建 lambda"></a>创建 lambda</h3><h4 id="创建-IAM-role"><a href="#创建-IAM-role" class="headerlink" title="创建 IAM role"></a>创建 IAM role</h4><p>创建 Lambda的IAM Role是必须的,他指定了当前lamda能访问到的资源有那些,从我们的列子中,我们需要用到DynamoDB, 同时为了方便debug,我们还需要用到cloudwatch服务 (这个对于查找问题非常有用)。</p>
<p>登录aws console,打开 Service 找到 IAM ,再选择Roles,点击 create role 按钮 后如图,(第三步可选):<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-lambda-create-role-step1.png" alt="image"><br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-lambda-create-role-step2.png" alt="image"><br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-lambda-create-role-step4.png" alt="image"> </p>
<h4 id="创建-lambda-函数"><a href="#创建-lambda-函数" class="headerlink" title="创建 lambda 函数"></a>创建 lambda 函数</h4><p>打开Service 找到lambda, 选择 create function:<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-create-lambda.png" alt="image"></p>
<p>创建后,在代码输入种类中选择上传 .zip 文件:<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-lambda-manage.png" alt="image"></p>
<p>将服务端代码整个打包 (注意一定要包括packages目录下的所有文件)然后上传,大小不能超过10m,如果超过了,可以在代码输入种类选择s3上传。上传完成后,指定入口文件(即在处理程序)为 lambda.handler, 此文件将会映射到 lambda.js文件,一般情况,如果上传的zip包不是很大,aws会自动列出zip项目目录可供在线编辑,但如果大了的化,比如好几兆,则有可能不会列出项目目录,每次修改又只能重新上传。</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-lambda-list-file.png" alt="image"></p>
<p>当然,如果node 代码里包括了一些环境变量,你也可以为 lambda 做一些环境变量的设置:</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-lambda-env.png" alt="image"></p>
<p>一切ok后,就可以测试了,关于lambda的测试,则相对还比较麻烦,我也是最近才稍微懂那么一点。</p>
<h4 id="测试-lambda-函数"><a href="#测试-lambda-函数" class="headerlink" title="测试 lambda 函数"></a>测试 lambda 函数</h4><p>在创建好的lambda 函数旁,点击配置测试事件按钮,在弹出对话框创建测试事件中选择创建新测试事件,在事件模板中选择 Amazon API Gateway AWS Proxy, 并给个测试名称,如图:<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-lambda-create-test.png" alt="image"></p>
<p>选择Amazon API Gateway AWS Proxy是因为我们的这个lambda函数最终会被API Gateway 触发调用,同时由于默认的事件模板是 post 的请求方式,而我们的这个服务端只有一个api/contacts的get方法,所以我们需要更改事件内容为:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "resource": "/{proxy+}",</span><br><span class="line"> "path": "/api/contacts",</span><br><span class="line"> "httpMethod": "get",</span><br><span class="line"> "headers": {</span><br><span class="line"> "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",</span><br><span class="line"> "Accept-Encoding": "gzip, deflate, sdch",</span><br><span class="line"> "Accept-Language": "en-US,en;q=0.8",</span><br><span class="line"> "Cache-Control": "max-age=0",</span><br><span class="line"> "CloudFront-Forwarded-Proto": "https",</span><br><span class="line"> "CloudFront-Is-Desktop-Viewer": "true",</span><br><span class="line"> "CloudFront-Is-Mobile-Viewer": "false",</span><br><span class="line"> "CloudFront-Is-SmartTV-Viewer": "false",</span><br><span class="line"> "CloudFront-Is-Tablet-Viewer": "false",</span><br><span class="line"> "CloudFront-Viewer-Country": "US",</span><br><span class="line"> "Host": "1234567890.execute-api.ap-northeast-1.amazonaws.com",</span><br><span class="line"> "Upgrade-Insecure-Requests": "1",</span><br><span class="line"> "User-Agent": "Custom User Agent String",</span><br><span class="line"> "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",</span><br><span class="line"> "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",</span><br><span class="line"> "X-Forwarded-For": "127.0.0.1, 127.0.0.2",</span><br><span class="line"> "X-Forwarded-Port": "443",</span><br><span class="line"> "X-Forwarded-Proto": "https"</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>保存测试事件,并点击执行,如果一切正常,会得到如下:<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-12-lambda-test-success.png" alt="image"></p>
<h3 id="创建-API-Gateway"><a href="#创建-API-Gateway" class="headerlink" title="创建 API Gateway"></a>创建 API Gateway</h3><p>找到Service下到API Gateway,并点击新建 api, </p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-create-api-gateway.png" alt="image"><br>新增 api 资源(路径)<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-api-gateway-create-api-source.png" alt="image"><br>选择 api 资源,再新增子资源,并选为proxy<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-api-gateway-create-source.png" alt="image"><br>选择 proxy 资源,创建 集成环境为我们创建好的lambda 函数<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-api-gateway-create-method.png" alt="image"> </p>
<p>创建完成之后,在操作选项中,选择部署,弹出对话框并命名为dev阶段:<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-api-gateway-deploy.png" alt="image"> </p>
<p>部署完成后,得到如下结果:<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-api-gateway-deploy-success.png" alt="image"></p>
<h3 id="API-Gateway-测试"><a href="#API-Gateway-测试" class="headerlink" title="API Gateway 测试"></a>API Gateway 测试</h3><p>在部署完成后,我们会在上述结果中得到发布出来的api 地址为 </p>
<blockquote>
<p><a href="https://ijihnuupmh.execute-api.ap-northeast-1.amazonaws.com/dev" target="_blank" rel="noopener">https://ijihnuupmh.execute-api.ap-northeast-1.amazonaws.com/dev</a></p>
</blockquote>
<p>此时如果直接访问,会得到Missing Authentication Token的错误,原因是我们地址不对<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">{"message":"Missing Authentication Token"}</span><br></pre></td></tr></table></figure></p>
<p>正确地址应该为:</p>
<blockquote>
<p><a href="https://ijihnuupmh.execute-api.ap-northeast-1.amazonaws.com/dev/api/contacts" target="_blank" rel="noopener">https://ijihnuupmh.execute-api.ap-northeast-1.amazonaws.com/dev/api/contacts</a></p>
</blockquote>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-api-gateway-deploy-url-success.png" alt="image"> </p>
<p>由于上述地址是永久的,除非你重新部署,所以我们可以放心的使用用来作为api地址。还有一个就是API Gateway似乎是不收取费用的,只会按照lambda函数的调用次数来收取费用,好像每月前100万次请求是免费的。所以还是相当划算。</p>
<p>OK! 一个无服务器的后端 api 就这样搭建好了,剩下的就是前端静态资源的托管了</p>
<h3 id="前端静态资源"><a href="#前端静态资源" class="headerlink" title="前端静态资源"></a>前端静态资源</h3><p>直接上传html,js,css 等静态资源到S3就好了,具体可以参见另一篇博客 <a href="https://troyyang.com/2018/05/12/aws_s3_https_static_website/">AWS系列之S3 + Cloudfront搭建https静态网站</a></p>
]]></content>
<summary type="html">
<hr>
<h3 id="Serverless-有什么用啊?"><a href="#Serverless-有什么用啊?" class="headerlink" title="Serverless 有什么用啊?"></a>Serverless 有什么用啊?</h3><p>Jason
</summary>
<category term="aws" scheme="http://troyyang.com/categories/aws/"/>
<category term="aws" scheme="http://troyyang.com/tags/aws/"/>
<category term="serverless" scheme="http://troyyang.com/tags/serverless/"/>
</entry>
<entry>
<title>AWS系列之S3 + Cloudfront搭建https静态网站</title>
<link href="http://troyyang.com/2018/05/12/aws_s3_https_static_website/"/>
<id>http://troyyang.com/2018/05/12/aws_s3_https_static_website/</id>
<published>2018-05-12T09:37:22.000Z</published>
<updated>2024-12-26T13:30:32.276Z</updated>
<content type="html"><![CDATA[<hr>
<p>本文和之前写的<a href="https://troyyang.com/2018/02/16/hosting-images-with-aws-s3/">《正确使用AWS S3的方式之打造自己的https图床》</a> 内容非常像,但也有新的内容如自动上传部署和自定义证书、Route53部分,这里主要补充新的内容。</p>
<h3 id="温馨提醒"><a href="#温馨提醒" class="headerlink" title="温馨提醒"></a>温馨提醒</h3><ol>
<li>个人用户请注册AWS 全球账号,因为AWS 中国账号似乎只对企业开发而无法注册。</li>
<li>AWS S3默认地址中国无法访问,需要使用Cloudfront的新地址才能访问,而且访问速度不慢,你能感觉到我博客的图片加载慢吗?</li>
<li>S3、Cloudfront、Route53等收费异常的低,几乎可以忽略。</li>
</ol>
<h3 id="架构概述"><a href="#架构概述" class="headerlink" title="架构概述"></a>架构概述</h3><p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-5-12-myenglishtutor-s3.png" alt="image"><br>采用Hexo作为静态网站生成器,主题使用的正是我自己开发的hexo-theme-twentyfifteen-wordpress,整个网站代码托管在github的私人repo下。当写好文章后,使用Hexo生成静态代码html+css+js+image,并使用写好的s3 SDK 上传工具到指定aws 的存储桶里进行静态托管 (之前使用的是github的Travis 自动部署,但由于私人Repo需要收费,于是放弃Travis改用自己的工具上传)。上传到S3里之后,使用Cloudfront做内容分发,并绑定自定义的https证书,最后,使用Route53做自定义域名的绑定。</p>
<h3 id="生成-IAM-access-key-用户子账号"><a href="#生成-IAM-access-key-用户子账号" class="headerlink" title="生成 IAM access key 用户子账号"></a>生成 IAM access key 用户子账号</h3><p>此账号可用于编程的方式访问AWS 的所有指定资源,这里我们创建的IAM 账号只需要有S3的读写权限</p>
<p>进入 <a href="https://console.aws.amazon.com/iam/home?region=ap-northeast-1#/users" target="_blank" rel="noopener">https://console.aws.amazon.com/iam/home?region=ap-northeast-1#/users</a></p>
<p>选择add user后, 一定要选择programmatic access这种编程方式的子账号,而另一个console账号针对的是用户名,密码登录的子账号<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-5-12-aws-iam-step1.png" alt="image"></p>
<p>指定该账号可访问的权限<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-5-12-aws-iam-step2.png" alt="image"></p>
<p>保存access id和key<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-5-12-aws-iam-step4.png" alt="image"></p>
<h3 id="使用S3-SDK自动上传"><a href="#使用S3-SDK自动上传" class="headerlink" title="使用S3 SDK自动上传"></a>使用S3 SDK自动上传</h3><p>默认情况下,所有Hexo编译后的文件都放在public文件件下,所以只需要拷贝到S3 存储桶下,当然可以手动拷贝,但是实在太麻烦。所以写了个node tool去自动上传(部署)。</p>
<p><a href="https://gist.github.com/Troy-Yang/436a62fb14d9e07e1aa3534f1c351050" target="_blank" rel="noopener">下载</a><br>–s3-deploy<br>——config.json<br>——index.js</p>
<p>config.js 里包括的是AWS AMI 账号信息,确保region使正确的区域, 具体参考<a href="https://docs.aws.amazon.com/zh_cn/AWSEC2/latest/UserGuide/using-regions-availability-zones.html" target="_blank" rel="noopener">区域列表</a><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "accessKeyId": "xxxxxx",</span><br><span class="line"> "secretAccessKey": "xxxxx",</span><br><span class="line"> "region": "ap-northeast-1" </span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>index.js,其中myenglishtutor.eu是存储桶的名字(命名的时候请用域名)<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">var path = require("path");</span><br><span class="line">var fs = require('fs');</span><br><span class="line">var mime = require('mime');</span><br><span class="line">var AWS = require('aws-sdk');</span><br><span class="line">AWS.config.loadFromPath('./s3-deploy/config.json');</span><br><span class="line">let s3 = new AWS.S3();</span><br><span class="line"></span><br><span class="line">const uploadDir = function (s3Path, bucketName) {</span><br><span class="line"> function walkSync(currentDirPath, callback) {</span><br><span class="line"> fs.readdirSync(currentDirPath).forEach(function (name) {</span><br><span class="line"> var filePath = path.join(currentDirPath, name);</span><br><span class="line"> var stat = fs.statSync(filePath);</span><br><span class="line"> if (stat.isFile()) {</span><br><span class="line"> callback(filePath, stat);</span><br><span class="line"> } else if (stat.isDirectory()) {</span><br><span class="line"> walkSync(filePath, callback);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> walkSync(s3Path, function (filePath, stat) {</span><br><span class="line"> let bucketPath = filePath.substring(s3Path.length + 1);</span><br><span class="line"> let mimeType = mime.getType(bucketPath);</span><br><span class="line"> let params = { </span><br><span class="line"> Bucket: bucketName, </span><br><span class="line"> Key: bucketPath.replace(/\\/g, '/'), </span><br><span class="line"> Body: fs.readFileSync(filePath),</span><br><span class="line"> ContentType: mimeType</span><br><span class="line"> };</span><br><span class="line"> s3.putObject(params, function (err, data) {</span><br><span class="line"> if (err) {</span><br><span class="line"> console.log(err)</span><br><span class="line"> } else {</span><br><span class="line"> console.log('Successfully uploaded ' + bucketPath + ' to ' + bucketName);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">uploadDir("public", "myenglishtutor.eu");</span><br></pre></td></tr></table></figure></p>
<p>最后在package.json文件中,添加运行脚本:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">"scripts": {</span><br><span class="line"> "start": "hexo clear & hexo g & hexo server",</span><br><span class="line"> "deploy": "hexo clear & hexo g & node ./s3-deploy/index"</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p>
<h3 id="使用Travis-自动部署"><a href="#使用Travis-自动部署" class="headerlink" title="使用Travis 自动部署"></a>使用Travis 自动部署</h3><p>如果代码是托管到github上或者支持Travis的服务,可以是用下面的.travis.yml配置达到CI, CD,请在travis.org中配置好环境变量$AWS_ACCESS_ID, AWS_SECRET_KEY,AWS_REGION<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">language: node_js</span><br><span class="line">node_js: stable</span><br><span class="line"></span><br><span class="line">script: true</span><br><span class="line"></span><br><span class="line"># S: Build Lifecycle</span><br><span class="line">install:</span><br><span class="line"> - npm install</span><br><span class="line"></span><br><span class="line">before_install:</span><br><span class="line"> - git submodule update --init --remote --recursive</span><br><span class="line"> </span><br><span class="line">#before_script:</span><br><span class="line"> # - npm install -g gulp</span><br><span class="line"></span><br><span class="line">script:</span><br><span class="line"> - hexo g</span><br><span class="line"># E: Build LifeCycle</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">before_deploy:</span><br><span class="line"> # - zip -r latest *</span><br><span class="line"> # - mkdir -p dpl_cd_upload</span><br><span class="line"> # - mv latest.zip dpl_cd_upload/latest.zip</span><br><span class="line"> - cp -a source/.well-known public/</span><br><span class="line"></span><br><span class="line">deploy:</span><br><span class="line"> - provider: s3</span><br><span class="line"> access_key_id: ${AWS_ACCESS_ID}</span><br><span class="line"> secret_access_key: ${AWS_SECRET_KEY}</span><br><span class="line"> local_dir: public</span><br><span class="line"> skip_cleanup: true</span><br><span class="line"> on:</span><br><span class="line"> repo: Troy-Yang/troy-yang.github.io</span><br><span class="line"> branch: source</span><br><span class="line"> bucket: troyyang.com</span><br><span class="line"> region: ${AWS_REGION}</span><br></pre></td></tr></table></figure></p>
<h3 id="https自定义证书"><a href="#https自定义证书" class="headerlink" title="https自定义证书"></a>https自定义证书</h3><p>想要支持https,那么证书是必须的,可以直接开启cloudfront,默认就会添加cloufront生成证书,如<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-2-18-distribution-overview.png" alt="image"></p>
<p>如果想要自定义证书,则需要自己在ACM(AWS Certificate Manager)申请证书,并做txt域名验证,一切ok后则会得到:<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-5-12-aws-myenglishtutur-acm.png" alt="image"></p>
<p>申请步骤如下:<br>进入 <a href="https://console.aws.amazon.com/acm/home?region=us-east-1#/wizard/" target="_blank" rel="noopener">https://console.aws.amazon.com/acm/home?region=us-east-1#/wizard/</a><br>填写域名:<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-5-12-aws-acm-step1.png" alt="image"><br>选择验证方式:(DNS验证方便,Email没试过,好像很麻烦)<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-5-12-aws-acm-step2.png" alt="image"><br>拷贝name和value值,保存并在DNS服务器中添加CNAME记录,如果DNS是使用AWS自家的Route53,则非常方便,只需要在相应的Domain下,添加 Record Set 记录, 类型选择CNAME。如果是在万网或者Cloudflare,也是非常方便的。<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-5-12-aws-acm-step4.png" alt="image"><br>记录添加好后,等待验证通过后(大概几至十几个小时后),状态从pending 变为 issued,就说明证书通过,该域名已合法。<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-5-12-aws-acm-list.png" alt="image"></p>
<h4 id="Cloudfront-中使用自定义证书"><a href="#Cloudfront-中使用自定义证书" class="headerlink" title="Cloudfront 中使用自定义证书"></a>Cloudfront 中使用自定义证书</h4><p>证书有了之后,只需要将其添加到创建的Cloudfront中就可以了<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-5-13-aws-acm-cloudfront.png" alt="image"></p>
<p>请注意选择Custom SSL Certificate, 然后输入框中,AWS会自动列出可用的证书列表,如果没有,则点击Request or Import a certificate with ACM 选择上面新增的就好了<br>在浏览器访问这个cloudfront地址,就可以看到https的标志,查看这个https证书就可以得到自定义的这个域名,而不是cloudfront开头的,看起来是不是很高大上。</p>
<h4 id="绑定Cloudfront-到自定义DNS"><a href="#绑定Cloudfront-到自定义DNS" class="headerlink" title="绑定Cloudfront 到自定义DNS"></a>绑定Cloudfront 到自定义DNS</h4><p>上面的cloudfront地址如果用于提供API之类的接口地址倒是无所谓, 但是如果是别人访问的地址,那肯定不行的,还需要添加一条A记录,将自己的域名和上述地址进行绑定。同理,如果是在Route53,只需要添加一条A记录就好了,大致如图:<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-5-13-aws-dns-cloudfront.png" alt="image"></p>
<p>保存后,过几分钟就可以通过自己域名,访问到S3中的内容,并且证书显示的是自己域名。<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-5-12-aws-myenglishtutur-acm.png" alt="image"></p>
]]></content>
<summary type="html">
<hr>
<p>本文和之前写的<a href="https://troyyang.com/2018/02/16/hosting-images-with-aws-s3/">《正确使用AWS S3的方式之打造自己的https图床》</a> 内容非常像,但也有新的内容如自动上传部署和自
</summary>
<category term="aws" scheme="http://troyyang.com/categories/aws/"/>
<category term="https" scheme="http://troyyang.com/tags/https/"/>
<category term="hexo" scheme="http://troyyang.com/tags/hexo/"/>
<category term="aws" scheme="http://troyyang.com/tags/aws/"/>
</entry>
<entry>
<title>AWS系列之 myenglishtutor基于AWS生态的广泛使用</title>
<link href="http://troyyang.com/2018/05/12/aws_structure_series/"/>
<id>http://troyyang.com/2018/05/12/aws_structure_series/</id>
<published>2018-05-12T09:37:22.000Z</published>
<updated>2024-12-26T13:30:32.277Z</updated>
<content type="html"><![CDATA[<hr>
<h3 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h3><p>看着系统生成的写作时间,2018年5月12日,恐怕是让所有四川人都难以忘怀的日子,在此缅怀十年前大地震遇难的同胞,也希望曾遭受苦痛的同胞十年后的今天一切安好!</p>
<p>回想在给Jason兼职工作的这半年多,一个人把 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://myenglishtutor.eu/images/icons/favicon-32x32.png" alt="image"> <a href="https://myenglishtutor.eu" target="_blank" rel="noopener">https://myenglishtutor.eu</a> 从0到1的把网站一点一点建好,感觉像个自己的孩子,总想要给他最好的,但也由于个人精力有限,很多想做的都没去做。尤其是AWS的生态让我印象深刻,所以想要写下这一个系列的技术感悟。</p>
<table>
<thead>
<tr>
<th style="text-align:left"><h2><a href="https://troyyang.com/2018/05/12/aws_s3_https_static_website/">AWS系列之S3 + Cloudfront搭建https hexo静态网站</a></h2></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">使用hexo做静态内容生成,s3托管静态网站内容,使用cloudfront做内容分发及https证书自动生成,route53做域名DNS解析,还有部分工具如依托s3 SDK做代码自动上传部署,google driver 自动同步等。</td>
</tr>
<tr>
<td style="text-align:left">hexo、s3、cloudfront、route53、certification、aws s3 SDK auto sync file and deployment</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th style="text-align:left"><h2><a href="https://troyyang.com/2018/01/21/stripe_guide_alipay/">AWS系列之 Stripe 国际支付</a></h2></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">服务端使用Lambda、API Gateway实现的无服务器服务,客户端使用Stripe的Element.js类库</td>
</tr>
<tr>
<td style="text-align:left">Lambda、API Gateway</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th style="text-align:left"><h2>AWS系列之结合OpenTek实现多人实时Web视频通话、教学</h2></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">使用s3做视频前端和后台管理的代码托管, Lambda+DynamoDB+API Gateway实现Serverless搭建的node express无服务器服务端。</td>
</tr>
<tr>
<td style="text-align:left">s3、openTek SDK、Lambda、DynamoDB、API Gateway、Angular 1.0、bootstrap</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th style="text-align:left"><h2>AWS系列之Polly服务实现AI文本到语音翻译</h2></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">使用aws的Polly服务实现文本转语音的翻译,服务端搭建的Serverless服务端,客户端使用自己写的hexo plugin功能调用API。</td>
</tr>
<tr>
<td style="text-align:left">Lambda、API Gateway、SNS、s3、Hexo plugin</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th style="text-align:left"><h2>AWS系列之使用Wowza streaming实现视频直播+弹幕服务</h2></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">架构上使用EC2负载均衡和自动扩容实现可伸缩视频直播服务。弹幕使用web socket实现双向通信,客户端采用对手机H5播放支持。</td>
</tr>
<tr>
<td style="text-align:left">s3、EC2、Load banance、ASG、cloudfront、linux、 danmaku、h5 video</td>
</tr>
</tbody>
</table>
<p>这里也打个广告简单介绍一下Jason和他的myenglishtutor,从名字也大概知道他是个英语老师和他的个人网站,Jason是个地道的英国人,浓厚的英国口音以及超过十几年英语专业教学经验,作为myenglishtutor的开发者,我为他集成了包括视频直播,1对1,1对多的视频教学直播,支付宝支付,词法解释等功能,所以不用担心教学方式及支付等问题。如果有意专业英语要求的个人或者团体,欢迎直接联系我,有更低的折扣等你。</p>
]]></content>
<summary type="html">
<hr>
<h3 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h3><p>看着系统生成的写作时间,2018年5月12日,恐怕是让所有四川人都难以忘怀的日子,在此缅怀十年前大地震遇难的同胞,
</summary>
<category term="aws" scheme="http://troyyang.com/categories/aws/"/>
<category term="aws" scheme="http://troyyang.com/tags/aws/"/>
</entry>
<entry>
<title>正确使用AWS S3的方式之打造自己的https图床</title>
<link href="http://troyyang.com/2018/02/16/hosting-images-with-aws-s3/"/>
<id>http://troyyang.com/2018/02/16/hosting-images-with-aws-s3/</id>
<published>2018-02-16T22:01:22.000Z</published>
<updated>2024-12-26T13:30:32.277Z</updated>
<content type="html"><![CDATA[<hr>
<p>写过博客的人都知道图床,一个托管自己博客图片的地方,当然托管到自己的服务器另当别论。常见的图床可以是新浪博客,七牛云,imgur等,但是都是有各种问题,比如我之前使用的是七牛云(也曾在<a href="https://troyyang.com/2017/05/21/Add_Free_Certification_In_Blog_Step_By_Step/">《给Github自定义域名加上HTTPS》</a>博文上推荐使用),用起来相当不错,只可惜后来备案信息过期了,导致无法再使用自定义域名,更可悲的是,https不再支持,意味着尽管我的博客是https但由于有内容是http的,只能被浏览器认为是mixed-content的。</p>
<p>但是,前几天无意发现一片新大陆,使用aws s3结合cloudfront distribution 可以借助亚马逊云无缝快速托管自己的图片还自带https,而费用几乎是很小的,按量收费。</p>
<h3 id="步骤概述"><a href="#步骤概述" class="headerlink" title="步骤概述"></a>步骤概述</h3><p>(如果不需要有自定义图片的域名,第三步可选)</p>
<ol>
<li>创建一个图片s3 bucket并公开。</li>
<li>创建cloudfront distribution并绑定S3 bucket和默认证书以支持https</li>
<li>在DNS服务商(我的是cloudflare)创建图床域名,并绑定cloudfront域名地址 </li>
</ol>
<h3 id="全球亚马逊-Or-亚马逊中国?"><a href="#全球亚马逊-Or-亚马逊中国?" class="headerlink" title="全球亚马逊 Or 亚马逊中国?"></a>全球亚马逊 Or 亚马逊中国?</h3><p>两者区别好像挺大的,后者曾经注册过,但是不知为什么没通过审核,可能需要公司邮箱吧。并且,如果考虑到备案等因素,建议使用全球亚马逊。(需要绑定VISA信用卡)<br>全球亚马逊地址是:<a href="https://console.aws.amazon.com/console/home" target="_blank" rel="noopener">https://console.aws.amazon.com/console/home</a></p>
<h3 id="创建S3-Bucket(存储桶)"><a href="#创建S3-Bucket(存储桶)" class="headerlink" title="创建S3 Bucket(存储桶)"></a>创建S3 Bucket(存储桶)</h3><p>账号创建成功后,进入S3控制台<a href="https://s3.console.aws.amazon.com,存储桶名称以待托管域名命名,比如我的是" target="_blank" rel="noopener">https://s3.console.aws.amazon.com,存储桶名称以待托管域名命名,比如我的是</a> images.troyyang.com,其他项首先都选择默认,待会再一项一项改。<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-2-18-s3-bucket.png" alt="image"></p>
<h4 id="访问权限设置"><a href="#访问权限设置" class="headerlink" title="访问权限设置"></a>访问权限设置</h4><p>在存储桶的权限页面,选择存储桶策略,键入下面的值:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "Version": "2012-10-17",</span><br><span class="line"> "Statement": [</span><br><span class="line"> {</span><br><span class="line"> "Sid": "PublicReadForGetBucketObjects",</span><br><span class="line"> "Effect": "Allow",</span><br><span class="line"> "Principal": "*",</span><br><span class="line"> "Action": "s3:GetObject",</span><br><span class="line"> "Resource": "arn:aws:s3:::images.troyyang.com/*"</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-2-18-aws-s3-permission.png" alt="image"></p>
<h4 id="静态托管"><a href="#静态托管" class="headerlink" title="静态托管"></a>静态托管</h4><p>存储桶创建成功后,进入属性页面,选择静态网站托管,键入索引文件index.html,错误文档error.html,然后保存。此时,公共访问页面已经生成,终端节点如下:<br><a href="http://images.troyyang.com.s3-website-ap-northeast-1.amazonaws.com" target="_blank" rel="noopener">http://images.troyyang.com.s3-website-ap-northeast-1.amazonaws.com</a></p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-2-18-aws-s3-static-host.png" alt="image"><br>上传自己的所有图片在此存储桶下,然后加上文件后缀就应该可以访问了,然而现实是残酷的,在我大天朝下,这个地址有时候是无法访问当的 。。。WTF。。。于是,得进行下面的自定义域名步骤</p>
<h3 id="CloudFront-Distribution"><a href="#CloudFront-Distribution" class="headerlink" title="CloudFront Distribution"></a>CloudFront Distribution</h3><p>上面地址是AWS自动生成的访问域名,并且只支持http,想要支持https,并且绑定自定义域名(images.troyyang.com),需要使用到CloudFront Distribution。</p>
<p>CloudFront Distribution 是AWS的内容分发(CDN)使得全球各地都能以最快的速度访问到AWS最近的节点(对于中国,最近的是东京,经测,也已经足够快),并且可绑定或者生产SSL证书。</p>
<h4 id="创建-Distribution"><a href="#创建-Distribution" class="headerlink" title="创建 Distribution"></a>创建 Distribution</h4><p>打开 <a href="https://console.aws.amazon.com/cloudfront/home," target="_blank" rel="noopener">https://console.aws.amazon.com/cloudfront/home,</a> 选择Create Distribution, 传输方式选择Web选项 Get Started,在很多选项中,主要注意几项就好了(都是可后期修改):</p>
<ul>
<li>Origin Domain Name中选择刚才所建的S3 Bucket 域名</li>
<li>Alternate Domain Names(CNAMEs)填写自定义域名(没有的话,可不管), 这里是 images.troyyang.com</li>
<li>SSL Certificate 暂时选默认Default CloudFront Certificate (*.cloudfront.net)</li>
<li>Price Class 可以只选择Use Only US, Canada, Europe and Asia</li>
</ul>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-2-18-aws-create-distribution.png" alt="image"></p>
<p>一切配置好后,静静等待几个小时就会看到Distribution部署成功,大致结果如下:</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-2-18-distribution-overview.png" alt="image"></p>
<p>此时,得到Distribution 的新访问地址 d2dxo9yo9kwqp2.cloudfront.net,这个时候,我们找一张在S3中存在的图片,加上https再次访问 <a href="https://d2dxo9yo9kwqp2.cloudfront.net/2017-5-21-https.png" target="_blank" rel="noopener">https://d2dxo9yo9kwqp2.cloudfront.net/2017-5-21-https.png</a> 一切OK</p>
<h4 id="自定义证书(可选)"><a href="#自定义证书(可选)" class="headerlink" title="自定义证书(可选)"></a>自定义证书(可选)</h4><p>上面的证书是亚马逊自己提供,如果想要使用绑定自己的域名证书,可以使用AWS的Certificate Manager 证书服务,在自己的DNS服务商比如万网或者阿里云那里配置好验证方式,具体操作方法参考 <a href="https://docs.aws.amazon.com/zh_cn/acm/latest/userguide/gs-acm-request.html" target="_blank" rel="noopener">https://docs.aws.amazon.com/zh_cn/acm/latest/userguide/gs-acm-request.html</a> 。因为我暂时觉得没必要,所以没使用上。</p>
<h3 id="绑定自定义域名(可选)"><a href="#绑定自定义域名(可选)" class="headerlink" title="绑定自定义域名(可选)"></a>绑定自定义域名(可选)</h3><p>上面的是cloudfront分发的一个地址,虽然地址是固定的,但毕竟不是自家的域名,感觉不高大上,所以需要绑定上自己的图片域名。</p>
<p>由于我的DNS服务解析改为了Cloudflare,所以是以Cloudflare的来配置的域名,但和万网或者阿里云的配置完全一致,在DNS解析项中添加一条CNAME记录,指向Cloudfront分配的域名即可</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-2-18-dns-image.png" alt="image"><br>等待绑定解析成功后,访问 <a href="https://images.troyyang.com/2017-5-21-https.png" target="_blank" rel="noopener">https://images.troyyang.com/2017-5-21-https.png</a> ,一切OK</p>
]]></content>
<summary type="html">
<hr>
<p>写过博客的人都知道图床,一个托管自己博客图片的地方,当然托管到自己的服务器另当别论。常见的图床可以是新浪博客,七牛云,imgur等,但是都是有各种问题,比如我之前使用的是七牛云(也曾在<a href="https://troyyang.com/2017/05/21
</summary>
<category term="aws" scheme="http://troyyang.com/categories/aws/"/>
<category term="https" scheme="http://troyyang.com/tags/https/"/>
<category term="aws" scheme="http://troyyang.com/tags/aws/"/>
<category term="devops" scheme="http://troyyang.com/tags/devops/"/>
<category term="s3" scheme="http://troyyang.com/tags/s3/"/>
</entry>
<entry>
<title>Stripe开发使用指南--国际支付(含支付宝)</title>
<link href="http://troyyang.com/2018/01/21/stripe_guide_alipay/"/>
<id>http://troyyang.com/2018/01/21/stripe_guide_alipay/</id>
<published>2018-01-21T11:08:49.000Z</published>
<updated>2024-12-26T13:30:32.278Z</updated>
<content type="html"><![CDATA[<hr>
<p>前段时间,因为Jason让我帮忙把Stripe支付集成到他个人网站上去,让我有机会接触到支付系统开发,同时也因为苦于没有找到太多中文方面相关文档介绍,所以做个总结,也方便以后有需要的同学。<br>(更新) 2021.5 发现好些同学也在咨询如何集成微信支付,其实也是非常简单,所以新增了最后微信的实现,见最后<br>(更新) 2022.8 Source的方式已经不被推荐,推荐使用PaymentIntent</p>
<h3 id="关于Stripe支付"><a href="#关于Stripe支付" class="headerlink" title="关于Stripe支付"></a>关于Stripe支付</h3><p>第一次听说Stripe还是在几个月前的一个新闻上了解到,大致说的是美国总统都在使用它,极有可能成功下一个Paypal。这么受欢迎的一个支付平台到底有什么好处呢?我粗略搜集了一下:</p>
<ul>
<li>一条代码让你网站支持繁琐的国际支付功能。(对于创业公司,再合适不过)</li>
<li>向全球化业务拓展会成为Stripe的机会。即使支付货币不同、方法不同,Stripe都能打通各自的渠道,让全球化交易不受支付阻碍。</li>
<li>市值超过90亿美元,和Tweeter,Lyft,Best Buy等以及国内的 Alipay, WeChat等有合作</li>
</ul>
<p>重点说下第二点,什么意思呢,就是说客户可以使用人民币支付,如果商家(收款方)是美国的银行的话,就自动转成美元,是英国的银行就自动转为英镑!(<strong>可惜暂时不支持商家是中国(但Stripe也可提供解决方案,就是使用Atlas去创建一个美国的代理公司)</strong>)</p>
<p>而对于我们程序员的话,当然最关心第一条,因为他的宗旨就是开发极简,对开发人员超级友好!至于多友好呢,请往下看。</p>
<h3 id="最简洁支付"><a href="#最简洁支付" class="headerlink" title="最简洁支付"></a>最简洁支付</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><!DOCTYPE html></span><br><span class="line"><html lang="en"></span><br><span class="line"><head></span><br><span class="line"> <meta charset="UTF-8"></span><br><span class="line"> <title>Stripe Checkout</title></span><br><span class="line"></head></span><br><span class="line"><body></span><br><span class="line"> <form action="/your-server-side-code" method="POST"></span><br><span class="line"> <script</span><br><span class="line"> src="https://checkout.stripe.com/checkout.js" class="stripe-button"</span><br><span class="line"> data-key="[Publishable key]"</span><br><span class="line"> data-amount="999"</span><br><span class="line"> data-name="troy yang"</span><br><span class="line"> data-description="Widget"</span><br><span class="line"> data-image="https://stripe.com/img/documentation/checkout/marketplace.png"</span><br><span class="line"> data-locale="auto"</span><br><span class="line"> data-zip-code="true"</span><br><span class="line"> data-currency="eur"></span><br><span class="line"> </script></span><br><span class="line"></form></span><br><span class="line"></body></span><br><span class="line"></html></span><br></pre></td></tr></table></figure>
<p>就这么几行代码,我们就已经实现了客户端所有事:</p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-1-13-stripe-checkout.png" alt="image"></p>
<p>真的是超级简单,但是这种方式是基于信用卡支付的界面,已经可以满足一半的支付方式,对于其他的三方支付,比如3D secure, 支付宝,微信,甚至比特币,Stripe为我们提供了其他方式,等下我就使用支付宝来举例。</p>
<h3 id="注册-Stripe-账号"><a href="#注册-Stripe-账号" class="headerlink" title="注册 Stripe 账号"></a>注册 Stripe 账号</h3><p>和注册支付宝账号一个道理,首先注册账号,然后绑定自己银行卡,BUT, 就像前面提到的,不支持中国,所以就算注册成功,也没法激活,也就没法收款。<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-1-13-stripe-support-countries.PNG" alt></p>
<p>对于中国商家怎么办呢,我能想到的就只有这几个办法:</p>
<ul>
<li>自己去支持国家去办理张银行卡</li>
<li>使用国外的朋友银行卡</li>
<li>使用Atlas</li>
</ul>
<p>对于Jason来说,因为他是英国人,所以他可以创建他的主账号,然后添加我的stripe账号到他team memeber账号列表中,这样我就可以访问他账户下所有开发者需要的权限。邀请成功后,Dashboard页面</p>
<h3 id="两个阶段"><a href="#两个阶段" class="headerlink" title="两个阶段"></a>两个阶段</h3><p>Stripe有两种模式,一个是测试模式(Test Mode),一个是生产模式(Live Mode),测试模式下产生的金钱交易都只用于测试,当所有测试通过后即可切换为Live模式。唯一的不同就是<strong>Publishable key</strong> 和 <strong>Secret key</strong>, 一会我们会用到这两个值。<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-1-13-stripe-test-mode.PNG" alt="image"></p>
<h3 id="交易流程"><a href="#交易流程" class="headerlink" title="交易流程"></a>交易流程</h3><p>Stripe有几个概念用于整个交易阶段和状态:<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-1-18-workflow.png" alt="image"></p>
<h4 id="创建-Source"><a href="#创建-Source" class="headerlink" title="创建 Source"></a>创建 Source</h4><p>使用自己的<strong>Publishable key</strong>来创建一种source(比如Cards, 3D Secure, 支付宝,甚至比特币等), 创建source完了后,就会得到一个用于交易的Token或者是一个跳转到其他支持的三方支付平台(比如支付宝支付)页面等待用户支付。当用户支付(或者取消支付)完成,自动跳转回到指定结果页面。用户支付页面结束后,可能会得到三个状态:</p>
<ul>
<li>source.chargeable 用户授权(支付)成功</li>
<li>source.failed 用户拒绝授权(支付)</li>
<li>source.canceled 超时支付</li>
</ul>
<h4 id="创建-Charge"><a href="#创建-Charge" class="headerlink" title="创建 Charge"></a>创建 Charge</h4><p>当用户支付成功后,此时在Stripe端的支付状态变为source.chargeable,意思就是授权成功了,你可以在我支付宝平台上扣钱啦,所以,此时我们还需要使用<strong>Secret key</strong>来创建Charge来完成,官方推荐的是使用webhooks来捕捉状态,并且完成Charge的创建。当Charge完成后,整个支付完成,会得到一个charge.succeeded的状态。</p>
<h4 id="使用-webhooks"><a href="#使用-webhooks" class="headerlink" title="使用 webhooks"></a>使用 webhooks</h4><p>Webhooks 里提供了几十种状态,所有这些状态都会注册到Stripe里一个叫webhooks事件钩子的地方,我们可以指定不同事件的触发时,转发数据到某个我们自己搭建好的Web Api上。(下图是我们的服务器end point, 因为我们没有用到服务器,使用的是亚马逊lambda做一个Serverless)<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-1-18-web-hooks.png" alt="image"></p>
<h2 id="举个支付宝的栗子"><a href="#举个支付宝的栗子" class="headerlink" title="举个支付宝的栗子"></a>举个支付宝的栗子</h2><h3 id="服务端-(Serverless)"><a href="#服务端-(Serverless)" class="headerlink" title="服务端 (Serverless)"></a>服务端 (Serverless)</h3><p>以AWS的Lambda + API gateway为例, 其中,前者是用来定义API, 后者是做路由。<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-1-18-lambda.png" alt="image"></p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-1-18-lambda-source-chargeable.png" alt="image"></p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-1-18-lambda-variable.png" alt="image"></p>
<p>创建Charge代码:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">'use strict';</span><br><span class="line"></span><br><span class="line">const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);</span><br><span class="line">exports.handler = (event, context, callback) => {</span><br><span class="line"> console.log("request: " + JSON.stringify(event));</span><br><span class="line"></span><br><span class="line"> let stripeData = event.data.object;</span><br><span class="line"> stripe.charges.create({</span><br><span class="line"> amount: stripeData.amount,</span><br><span class="line"> source: stripeData.id,</span><br><span class="line"> currency: stripeData.currency || 'usd',</span><br><span class="line"> description: 'My Englishtutor 30 days' || ('Stripe payment ' + event.id),</span><br><span class="line"> }, function(err, charge) {</span><br><span class="line"> if (err && err.type === 'card_error') {</span><br><span class="line"> context.fail(new Error(err.message));</span><br><span class="line"> }</span><br><span class="line"> else if (err) {</span><br><span class="line"> context.fail(err);</span><br><span class="line"> }</span><br><span class="line"> else {</span><br><span class="line"> context.succeed({ status: charge.status, success: true });</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p>
<h3 id="客户端-Web"><a href="#客户端-Web" class="headerlink" title="客户端 (Web)"></a>客户端 (Web)</h3><p>多种实现方式:</p>
<h4 id="Checkout"><a href="#Checkout" class="headerlink" title="Checkout"></a>Checkout</h4><p>文章开头那段<form>的集成代码就是使用的checkout方式,非常简单。集成代码直接帮你完成了客户端的部分,服务端只需要定义好source.chargeable的钩子API 就好了。</form></p>
<p>在做支付宝开发的时候,发现可以直接使用Checkout的方式:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><form action="https://xxx.execute-api.eu-central-1.amazonaws.com/stripepayment/xxx" method="POST"></span><br><span class="line"> <script</span><br><span class="line"> src="https://checkout.stripe.com/checkout.js" class="stripe-button"</span><br><span class="line"> data-key="pk_test_xxx"</span><br><span class="line"> data-amount="30000"</span><br><span class="line"> data-name="myenglishtutor.eu"</span><br><span class="line"> data-label="Pay With Alipay"</span><br><span class="line"> data-description="30 days"</span><br><span class="line"> data-image="/images/logo.png"</span><br><span class="line"> data-locale="auto"</span><br><span class="line"> data-alipay="auto"</span><br><span class="line"> data-currency="usd"></span><br><span class="line"> </script></span><br><span class="line"></form></span><br></pre></td></tr></table></figure></p>
<p>但是总是得到这个错误:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Unrecognized request URL (POST: /v1/alipay/send_sms). Please see https://stripe.com/docs or we can help at https://support.stripe.com/.</span><br></pre></td></tr></table></figure></p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-1-21-alipay-checkout.png" alt="image"></p>
<p>发邮件给Stripe support team得到的结果是为了以后的扩展,Stripe不再提供alipay的checkout方式, 无奈,只得使用下面的方式。</p>
<h4 id="Stripe-js-amp-Elements"><a href="#Stripe-js-amp-Elements" class="headerlink" title="Stripe.js & Elements"></a>Stripe.js & Elements</h4><p>当然对于如果你觉得Checkout的方式集成度太高,不够灵活,那Stripe.js是你最好的选择。</p>
<p>Stripe.js其实就是客户端的一个JS核心类库,Elements是它的UI类库,其实上面的Checkout代码就是Stripe使用两者给我们封装好了的,避免我们直接接触敏感信息,但是其实质都是一样的,都是用来创建source。这里就直接贴出客户端的代码了(这里没有用到Elements做UI,因为就是一个按钮支付,太简单,所以没用到):<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">var stripe = Stripe('pk_live_xxxx');</span><br><span class="line"></span><br><span class="line">function alipay(amount) {</span><br><span class="line"> showLoading();</span><br><span class="line"> stripe.createSource({</span><br><span class="line"> type: 'alipay',</span><br><span class="line"> amount: parseInt(amount),</span><br><span class="line"> currency: 'gbp', // usd, eur,</span><br><span class="line"> redirect: {</span><br><span class="line"> return_url: 'https://xxx.eu/pay/result.html'</span><br><span class="line"> },</span><br><span class="line"> }).then(function (response) {</span><br><span class="line"> hideLoading();</span><br><span class="line"> if (response.error) {</span><br><span class="line"> alert(response.error.message);</span><br><span class="line"> }</span><br><span class="line"> else {</span><br><span class="line"> processStripeResponse(response.source);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">function processStripeResponse(source) {</span><br><span class="line"> window.location.href = source.redirect.url;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-1-21-alipay-console.png" alt="image"></p>
<p>这里需要注意几点:</p>
<ul>
<li>currency 必须是Stripe账号所在地货币,也就是绑定的银行卡所在地,因为Jason是英国人,所以必须使用gbp(这里愚蠢如我的犯了一个常识错误,一直以为英国也是欧盟的,所以使用eur,结果怎么也不对,直接哭晕在厕所)</li>
<li>return_url指向的是当用户重定向到我们常见的支付宝支付页面后,跳转回支付完成的页面,在这个返回页面中,因为支付宝是同步完成支付的,所以我们可以去查询charge.succeeded的状态来判定是否用户支付是否完成。</li>
</ul>
<p>当一切OK,点击支付按钮,就会跳转到支付宝支付页面(其他支持的三方平台也可以),如下:<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-1-23-alipay-success.png" alt="image"></p>
<h3 id="微信实现"><a href="#微信实现" class="headerlink" title="微信实现"></a>微信实现</h3><p>其实也非常的简单,只需要将上一步的type改为wechat,同时返回source中的source.wechat.qr_code_url转为二维码就好了<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">var wechatCallback = function (source) {</span><br><span class="line"> generateQRCode(source.wechat.qr_code_url);</span><br><span class="line">}</span><br><span class="line">function generateQRCode(value) {</span><br><span class="line"> var qrEle = document.getElementById("qrcode");</span><br><span class="line"> var qrcode = new QRCode(qrEle, {</span><br><span class="line"> width: 100,</span><br><span class="line"> height: 100</span><br><span class="line"> });</span><br><span class="line"> qrcode.makeCode(value);</span><br><span class="line"> qrEle.style.display = 'inline-block';</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>二维码出来后, 扫码就会得到如下结果<br><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="https://images.troyyang.com/2018-12-16-wechat-success.jpeg" alt="image"> </p>
<p>(更新) 本人开发了一款wordpress stripe插件,主要针对微信和支付宝,涉及一些更新,详见另一篇文章<a href="https://troyyang.com/2020/12/30/wordpress-stripe-express-released/">Wordpress 插件 Stripe Express 发布啦!</a></p>
]]></content>
<summary type="html">
<hr>
<p>前段时间,因为Jason让我帮忙把Stripe支付集成到他个人网站上去,让我有机会接触到支付系统开发,同时也因为苦于没有找到太多中文方面相关文档介绍,所以做个总结,也方便以后有需要的同学。<br>(更新) 2021.5 发现好些同学也在咨询如何集成微信支付,其实也
</summary>
<category term="Web" scheme="http://troyyang.com/categories/Web/"/>
<category term="payment" scheme="http://troyyang.com/tags/payment/"/>
<category term="stripe" scheme="http://troyyang.com/tags/stripe/"/>
<category term="alipay" scheme="http://troyyang.com/tags/alipay/"/>
<category term="finance" scheme="http://troyyang.com/tags/finance/"/>
</entry>
<entry>
<title>成都持续交付大会</title>
<link href="http://troyyang.com/2017/12/10/cdconf/"/>
<id>http://troyyang.com/2017/12/10/cdconf/</id>